You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2796 lines
76 KiB
2796 lines
76 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
usbohci.c
|
|
|
|
Abstract:
|
|
|
|
USB OHCI 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 Microsoft Corporation. All Rights Reserved.
|
|
|
|
|
|
Revision History:
|
|
|
|
2-19-99 : created, jdunn
|
|
|
|
--*/
|
|
|
|
#include "common.h"
|
|
|
|
//implements the following miniport functions:
|
|
//OHCI_InitializeHardware
|
|
//OHCI_StartController
|
|
//OHCI_StopController
|
|
//OHCI_OpenEndpoint
|
|
//OHCI_QueryEndpointRequirements
|
|
//OHCI_PokeEndpoint
|
|
|
|
USB_MINIPORT_STATUS
|
|
OHCI_InitializeHardware(
|
|
PDEVICE_DATA DeviceData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the hardware registers for the host controller.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
HC_CONTROL control;
|
|
ULONG reg;
|
|
ULONG sofModifyValue;
|
|
LARGE_INTEGER finishTime, currentTime;
|
|
|
|
hc = DeviceData->HC;
|
|
|
|
//
|
|
// if we made it here then we now own the HC and can initialize it.
|
|
//
|
|
|
|
//
|
|
// Get current frame interval (could account for a known clock error)
|
|
//
|
|
DeviceData->BIOS_Interval.ul = READ_REGISTER_ULONG(&hc->HcFmInterval.ul);
|
|
|
|
// If FrameInterval is outside the range of the nominal value of 11999
|
|
// +/- 1% then assume the value is bogus and set it to the nominal value.
|
|
//
|
|
if ((DeviceData->BIOS_Interval.FrameInterval < 11879) ||
|
|
(DeviceData->BIOS_Interval.FrameInterval > 12119)) {
|
|
DeviceData->BIOS_Interval.FrameInterval = 11999; // 0x2EDF
|
|
}
|
|
|
|
//
|
|
// Set largest data packet (in case BIOS did not set)
|
|
//
|
|
DeviceData->BIOS_Interval.FSLargestDataPacket =
|
|
((DeviceData->BIOS_Interval.FrameInterval - MAXIMUM_OVERHEAD) * 6) / 7;
|
|
DeviceData->BIOS_Interval.FrameIntervalToggle ^= 1;
|
|
|
|
//
|
|
// do a hardware reset of the controller
|
|
//
|
|
WRITE_REGISTER_ULONG(&hc->HcCommandStatus.ul, HcCmd_HostControllerReset);
|
|
//
|
|
// Wait at least 10 microseconds for the reset to complete
|
|
//
|
|
KeStallExecutionProcessor(20);
|
|
|
|
//
|
|
// Take HC to USBReset state,
|
|
// NOTE: this generates global reset signaling on the bus
|
|
//
|
|
control.ul = READ_REGISTER_ULONG(&hc->HcControl.ul);
|
|
control.HostControllerFunctionalState = HcCtrl_HCFS_USBReset;
|
|
WRITE_REGISTER_ULONG(&hc->HcControl.ul, control.ul);
|
|
|
|
|
|
//
|
|
// Restore original frame interval, if we have a registry override
|
|
// value we use it instead.
|
|
//
|
|
|
|
// check for a registry based SOF modify Value.
|
|
// if we have one override the BIOS value
|
|
if (DeviceData->Flags & HMP_FLAG_SOF_MODIFY_VALUE) {
|
|
DeviceData->BIOS_Interval.FrameInterval =
|
|
DeviceData->SofModifyValue;
|
|
}
|
|
|
|
// for some reason writing this register does not always take on
|
|
// the hydra so we loop until it sticks
|
|
|
|
KeQuerySystemTime(&finishTime);
|
|
// figure when we quit (.5 seconds later)
|
|
finishTime.QuadPart += 5000000;
|
|
|
|
do {
|
|
|
|
WRITE_REGISTER_ULONG(&hc->HcFmInterval.ul, DeviceData->BIOS_Interval.ul);
|
|
reg = READ_REGISTER_ULONG(&hc->HcFmInterval.ul);
|
|
|
|
OHCI_KdPrint((DeviceData, 2, "'fi reg = %x set = %x\n", reg,
|
|
DeviceData->BIOS_Interval.ul));
|
|
|
|
KeQuerySystemTime(¤tTime);
|
|
|
|
if (currentTime.QuadPart >= finishTime.QuadPart) {
|
|
// half a second has elapsed ,give up and fail the hardware
|
|
OHCI_KdPrint((DeviceData, 0,
|
|
"'frame interval not set\n"));
|
|
|
|
LOGENTRY(DeviceData, G, '_fr!', 0, 0, 0);
|
|
return USBMP_STATUS_HARDWARE_FAILURE;
|
|
}
|
|
|
|
} while (reg != DeviceData->BIOS_Interval.ul);
|
|
|
|
OHCI_KdPrint((DeviceData, 2, "'fi = %x\n", DeviceData->BIOS_Interval.ul));
|
|
|
|
//
|
|
// Set the HcPeriodicStart register to 90% of the FrameInterval
|
|
//
|
|
WRITE_REGISTER_ULONG(&hc->HcPeriodicStart,
|
|
(DeviceData->BIOS_Interval.FrameInterval * 9 + 5)
|
|
/ 10);
|
|
|
|
// set the ptr to the HCCA
|
|
WRITE_REGISTER_ULONG(&hc->HcHCCA, DeviceData->HcHCCAPhys);
|
|
|
|
//
|
|
// Enable interrupts, this will not cause the controller
|
|
// to generate any because master-enable is not set yet.
|
|
//
|
|
WRITE_REGISTER_ULONG(&hc->HcInterruptEnable,
|
|
HcInt_OwnershipChange |
|
|
HcInt_SchedulingOverrun |
|
|
HcInt_WritebackDoneHead |
|
|
HcInt_FrameNumberOverflow |
|
|
HcInt_UnrecoverableError);
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
OHCI_StopBIOS(
|
|
PDEVICE_DATA DeviceData,
|
|
PHC_RESOURCES HcResources
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
ULONG hcControl;
|
|
|
|
hc = DeviceData->HC;
|
|
|
|
//
|
|
// Check to see if a System Management Mode driver owns the HC
|
|
//
|
|
|
|
hcControl = READ_REGISTER_ULONG(&hc->HcControl.ul);
|
|
|
|
if (hcControl & HcCtrl_InterruptRouting) {
|
|
|
|
OHCI_KdPrint((DeviceData, 1, "'detected Legacy BIOS\n"));
|
|
|
|
HcResources->DetectedLegacyBIOS = TRUE;
|
|
|
|
if ((hcControl == HcCtrl_InterruptRouting) &&
|
|
(READ_REGISTER_ULONG(&hc->HcInterruptEnable) == 0)) {
|
|
|
|
// Major assumption: If HcCtrl_InterruptRouting is set but
|
|
// no other bits in HcControl are set, i.e. HCFS==UsbReset,
|
|
// and no interrupts are enabled, then assume that the BIOS
|
|
// is not actually using the host controller. In this case
|
|
// just clear the erroneously set HcCtrl_InterruptRouting.
|
|
//
|
|
// This assumption appears to be correct on a Portege 3010CT,
|
|
// where HcCtrl_InterruptRouting is set during a Resume from
|
|
// Standby, but the BIOS doesn't actually appear to be using
|
|
// the host controller. If we were to continue on and set
|
|
// HcCmd_OwnershipChangeRequest, the BIOS appears to wake up
|
|
// and try to take ownership of the host controller instead of
|
|
// giving it up.
|
|
//
|
|
|
|
OHCI_KdPrint((DeviceData, 0,
|
|
"'HcCtrl_InterruptRouting erroneously set\n"));
|
|
|
|
WRITE_REGISTER_ULONG(&hc->HcControl.ul, 0);
|
|
|
|
} else {
|
|
|
|
LARGE_INTEGER finishTime, currentTime;
|
|
|
|
//
|
|
// A SMM driver does own the HC, it will take some time to
|
|
// get the SMM driver to relinquish control of the HC. We
|
|
// will ping the SMM driver, and then wait repeatedly until
|
|
// the SMM driver has relinquished control of the HC.
|
|
//
|
|
// THIS CODE ONLY WORKS IF WE ARE EXECUTING IN THE CONTEXT
|
|
// OF A SYSTEM THREAD.
|
|
//
|
|
|
|
// The HAL has disabled interrupts on the HC. Since
|
|
// interruptrouting is set we assume there is a functional
|
|
// smm BIOS. The BIOS will need the master interrupt
|
|
// enabled to complete the handoff (if it is disabled the
|
|
// machine will hang). So we re-enable the master interrupt
|
|
// here.
|
|
|
|
WRITE_REGISTER_ULONG(&hc->HcInterruptEnable,
|
|
HcInt_MasterInterruptEnable);
|
|
|
|
WRITE_REGISTER_ULONG(&hc->HcCommandStatus.ul,
|
|
HcCmd_OwnershipChangeRequest);
|
|
|
|
// hack for NEC -- disable the root hub status change
|
|
// to prevent an unhandled interrupt from being asserted
|
|
// after handoff
|
|
WRITE_REGISTER_ULONG(&hc->HcInterruptDisable,
|
|
HcInt_RootHubStatusChange);
|
|
// bugbug expose with service
|
|
KeQuerySystemTime(&finishTime);
|
|
// figure when we quit (.5 seconds later)
|
|
finishTime.QuadPart += 5000000;
|
|
|
|
//
|
|
// We told the SMM driver we want the HC, now all we can do is wait
|
|
// for the SMM driver to be done with the HC.
|
|
//
|
|
while (READ_REGISTER_ULONG(&hc->HcControl.ul) &
|
|
HcCtrl_InterruptRouting) {
|
|
|
|
KeQuerySystemTime(¤tTime);
|
|
|
|
if (currentTime.QuadPart >= finishTime.QuadPart) {
|
|
|
|
OHCI_KdPrint((DeviceData, 0,
|
|
"'SMM has not relinquised HC -- this is a HW bug\n"));
|
|
|
|
LOGENTRY(DeviceData, G, '_sm!', 0, 0, 0);
|
|
return USBMP_STATUS_HARDWARE_FAILURE;
|
|
}
|
|
}
|
|
|
|
// we have control, disable master interrupt until we
|
|
// finish intializing
|
|
WRITE_REGISTER_ULONG(&hc->HcInterruptStatus,
|
|
0xffffffff);
|
|
|
|
WRITE_REGISTER_ULONG(&hc->HcInterruptDisable,
|
|
HcInt_MasterInterruptEnable);
|
|
|
|
}
|
|
}
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
OHCI_InitializeSchedule(
|
|
PDEVICE_DATA DeviceData,
|
|
PUCHAR StaticEDs,
|
|
HW_32BIT_PHYSICAL_ADDRESS StaticEDsPhys,
|
|
PUCHAR EndCommonBuffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Build the schedule of static Eds
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
USB_MINIPORT_STATUS mpStatus;
|
|
ULONG length;
|
|
ULONG i;
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
|
|
//
|
|
// Allocate staticly disabled EDs, and set head pointers for
|
|
// scheduling lists
|
|
//
|
|
// The static ED list is contains all the static interrupt EDs (64)
|
|
// plus the static ED for bulk and control (2)
|
|
//
|
|
// the array looks like this:
|
|
// 1, 2, 2, 4, 4, 4, 4, 8,
|
|
// 8, 8, 8, 8, 8, 8, 8,16,
|
|
// 16,16,16,16,16,16,16,16,
|
|
// 16,16,16,16,16,16,16,32,
|
|
// 32,32,32,32,32,32,32,32,
|
|
// 32,32,32,32,32,32,32,32,
|
|
// 32,32,32,32,32,32,32,32,
|
|
// 32,32,32,32,32,32,32,
|
|
// CONTROL
|
|
// BULK
|
|
|
|
// each static ED points to another static ED
|
|
// (except for the 1ms ed) the INDEX of the next
|
|
// ED in the StaticEDList is stored in NextIdx,
|
|
// these values are constent
|
|
CHAR nextIdxTable[63] = {
|
|
// 0 1 2 3 4 5 6 7
|
|
(CHAR)ED_EOF, 0, 0, 1, 1, 2, 2, 3,
|
|
// 8 9 10 11 12 13 14 15
|
|
3, 4, 4, 5, 5, 6, 6, 7,
|
|
//16 17 18 19 20 21 22 23
|
|
7, 8, 8, 9, 9,10,10,11,
|
|
//24 25 26 27 28 29 30 31
|
|
11,12,12,13,13,14,14,15,
|
|
//32 33 34 35 36 37 38 39
|
|
15,16,16,17,17,18,18,19,
|
|
//40 41 42 43 44 45 46 47
|
|
19,20,20,21,21,22,22,23,
|
|
//48 49 50 51 52 53 54 55
|
|
23,24,24,25,25,26,26,27,
|
|
//56 57 58 59 60 61 62 63
|
|
27,28,28,29,29,30,30
|
|
};
|
|
|
|
/*
|
|
Numbers are the index into the static ed table
|
|
|
|
(31) -\
|
|
(15)-\
|
|
(32) -/ \
|
|
(7 )-\
|
|
(33) -\ / \
|
|
(16)-/ \
|
|
(34) -/ \
|
|
(3)-\
|
|
(35) -\ / \
|
|
(17)-\ / \
|
|
(36) -/ \ / \
|
|
(8 )-/ \
|
|
(37) -\ / \
|
|
(18)-/ \
|
|
(38) -/ \
|
|
(1)-\
|
|
(39) -\ / \
|
|
(19)-\ / \
|
|
(40) -/ \ / \
|
|
(9 )-\ / \
|
|
(41) -\ / \ / \
|
|
(20)-/ \ / \
|
|
(42) -/ \ / \
|
|
(4)-/ \
|
|
(43) -\ / \
|
|
(21)-\ / \
|
|
(44) -/ \ / \
|
|
(10)-/ \
|
|
(45) -\ / \
|
|
(22)-/ \
|
|
(46) -/ \
|
|
(0)
|
|
(47) -\ /
|
|
(23)-\ /
|
|
(48) -/ \ /
|
|
(11)-\ /
|
|
(49) -\ / \ /
|
|
(24)-/ \ /
|
|
(50) -/ \ /
|
|
(5)-\ /
|
|
(51) -\ / \ /
|
|
(25)-\ / \ /
|
|
(52) -/ \ / \ /
|
|
(12)-/ \ /
|
|
(53) -\ / \ /
|
|
(26)-/ \ /
|
|
(54) -/ \ /
|
|
(2)-/
|
|
(55) -\ /
|
|
(27)-\ /
|
|
(56) -/ \ /
|
|
(13)-\ /
|
|
(57) -\ / \ /
|
|
(28)-/ \ /
|
|
(58) -/ \ /
|
|
(6)-/
|
|
(59) -\ /
|
|
(29)-\ /
|
|
(60) -/ \ /
|
|
(14)-/
|
|
(61) -\ /
|
|
(30)-/
|
|
(62) -/
|
|
*/
|
|
|
|
// corresponding offsets for the 32ms list heads in the
|
|
// HCCA -- these are entries 31..62
|
|
ULONG used = 0;
|
|
CHAR Hcca32msOffsets[32] = {
|
|
0, 16, 8, 24, 4, 20, 12, 28,
|
|
2, 18, 10, 26, 6, 22, 14, 30,
|
|
1, 17, 9, 25, 5, 21, 13, 29,
|
|
3, 19, 11, 27, 7, 23, 15, 31
|
|
};
|
|
|
|
DeviceData->StaticEDs = StaticEDs;
|
|
DeviceData->StaticEDsPhys = StaticEDsPhys;
|
|
|
|
hc = DeviceData->HC;
|
|
|
|
// initailze all interrupt EDs
|
|
|
|
for (i=0; i<ED_CONTROL; i++) {
|
|
CHAR n;
|
|
PHW_ENDPOINT_DESCRIPTOR hwED;
|
|
|
|
//
|
|
// Carve EDs from the common buffer
|
|
//
|
|
hwED = (PHW_ENDPOINT_DESCRIPTOR) StaticEDs;
|
|
n = nextIdxTable[i];
|
|
|
|
// initialize the hardware ED
|
|
hwED->TailP = hwED->HeadP = 0xDEAD0000;
|
|
//hwED->TailP = hwED->HeadP = StaticEDsPhys;
|
|
hwED->Control = HcEDControl_SKIP; // ED is disabled
|
|
|
|
LOGENTRY(DeviceData, G, '_isc', n, &DeviceData->StaticEDList[0], 0);
|
|
|
|
if (n == (CHAR)ED_EOF) {
|
|
hwED->NextED = 0;
|
|
} else {
|
|
OHCI_ASSERT(DeviceData, n>=0 && n<31);
|
|
hwED->NextED = DeviceData->StaticEDList[n].HwEDPhys;
|
|
}
|
|
|
|
// initailze the list we use for real EDs
|
|
InitializeListHead(&DeviceData->StaticEDList[i].TransferEdList);
|
|
DeviceData->StaticEDList[i].HwED = hwED;
|
|
DeviceData->StaticEDList[i].HwEDPhys = StaticEDsPhys;
|
|
DeviceData->StaticEDList[i].NextIdx = n;
|
|
DeviceData->StaticEDList[i].EdFlags = EDFLAG_INTERRUPT;
|
|
|
|
// store address of hcc table entry
|
|
DeviceData->StaticEDList[i].PhysicalHead =
|
|
&hwED->NextED;
|
|
|
|
// next ED
|
|
StaticEDs += sizeof(HW_ENDPOINT_DESCRIPTOR);
|
|
StaticEDsPhys += sizeof(HW_ENDPOINT_DESCRIPTOR);
|
|
}
|
|
|
|
// now set the head pointers in the HCCA
|
|
// the HCCA points to all the 32ms list heads
|
|
for (i=0; i<32; i++) {
|
|
|
|
ULONG hccaOffset;
|
|
|
|
hccaOffset = Hcca32msOffsets[i];
|
|
|
|
DeviceData->HcHCCA->HccaInterruptTable[hccaOffset] =
|
|
DeviceData->StaticEDList[i+ED_INTERRUPT_32ms].HwEDPhys;
|
|
DeviceData->StaticEDList[i+ED_INTERRUPT_32ms].HccaOffset =
|
|
hccaOffset;
|
|
|
|
// physical head for 32ms list point to HCCA
|
|
DeviceData->StaticEDList[i+ED_INTERRUPT_32ms].PhysicalHead =
|
|
&DeviceData->HcHCCA->HccaInterruptTable[hccaOffset];
|
|
|
|
}
|
|
|
|
//
|
|
// Setup EDList entries for Control & Bulk
|
|
//
|
|
InitializeListHead(&DeviceData->StaticEDList[ED_CONTROL].TransferEdList);
|
|
DeviceData->StaticEDList[ED_CONTROL].NextIdx = (CHAR) ED_EOF;
|
|
DeviceData->StaticEDList[ED_CONTROL].PhysicalHead = &hc->HcControlHeadED;
|
|
DeviceData->StaticEDList[ED_CONTROL].EdFlags = EDFLAG_CONTROL | EDFLAG_REGISTER;
|
|
|
|
InitializeListHead(&DeviceData->StaticEDList[ED_BULK].TransferEdList);
|
|
DeviceData->StaticEDList[ED_BULK].NextIdx = (CHAR) ED_EOF;
|
|
DeviceData->StaticEDList[ED_BULK].PhysicalHead = &hc->HcBulkHeadED;
|
|
DeviceData->StaticEDList[ED_BULK].EdFlags = EDFLAG_BULK | EDFLAG_REGISTER;
|
|
|
|
if (DeviceData->ControllerFlavor == OHCI_Hydra) {
|
|
used = InitializeHydraHsLsFix(DeviceData, StaticEDs, StaticEDsPhys);
|
|
}
|
|
|
|
StaticEDs += used;
|
|
StaticEDsPhys += used;
|
|
|
|
OHCI_ASSERT(DeviceData, StaticEDs <= EndCommonBuffer);
|
|
|
|
mpStatus = USBMP_STATUS_SUCCESS;
|
|
|
|
return mpStatus;
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_GetRegistryParameters(
|
|
PDEVICE_DATA DeviceData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
sets the registry based sof modify value
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
USB_MINIPORT_STATUS mpStatus;
|
|
ULONG clocksPerFrame;
|
|
|
|
// get SOF modify value from registry
|
|
mpStatus =
|
|
USBPORT_GET_REGISTRY_KEY_VALUE(DeviceData,
|
|
TRUE, // software branch
|
|
SOF_MODIFY_KEY,
|
|
sizeof(SOF_MODIFY_KEY),
|
|
&clocksPerFrame,
|
|
sizeof(clocksPerFrame));
|
|
|
|
// if this call fails we just use the default
|
|
|
|
if (mpStatus == USBMP_STATUS_SUCCESS) {
|
|
SET_FLAG(DeviceData->Flags, HMP_FLAG_SOF_MODIFY_VALUE);
|
|
DeviceData->SofModifyValue = clocksPerFrame;
|
|
OHCI_KdPrint((DeviceData, 1, "'Recommended Clocks/Frame %d \n",
|
|
clocksPerFrame));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
USBMPFN
|
|
OHCI_StopController(
|
|
PDEVICE_DATA DeviceData,
|
|
BOOLEAN HwPresent
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
HC_CONTROL control;
|
|
PHC_OPERATIONAL_REGISTER hc = NULL;
|
|
|
|
hc = DeviceData->HC;
|
|
|
|
control.ul = READ_REGISTER_ULONG(&hc->HcControl.ul);
|
|
|
|
control.ul &= ~(HcCtrl_PeriodicListEnable |
|
|
HcCtrl_IsochronousEnable |
|
|
HcCtrl_ControlListEnable |
|
|
HcCtrl_BulkListEnable |
|
|
HcCtrl_RemoteWakeupEnable);
|
|
|
|
control.HostControllerFunctionalState =
|
|
HcHCFS_USBSuspend;
|
|
|
|
WRITE_REGISTER_ULONG(&hc->HcControl.ul, control.ul);
|
|
WRITE_REGISTER_ULONG(&hc->HcInterruptDisable, 0xFFFFffff);
|
|
WRITE_REGISTER_ULONG(&hc->HcInterruptStatus, 0xFFFFffff);
|
|
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
USBMPFN
|
|
OHCI_StartController(
|
|
PDEVICE_DATA DeviceData,
|
|
PHC_RESOURCES HcResources
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
USB_MINIPORT_STATUS mpStatus;
|
|
PHC_OPERATIONAL_REGISTER hc = NULL;
|
|
PUCHAR endCommonBuffer;
|
|
|
|
OHCI_KdPrint((DeviceData, 1, "'OPENHCI Miniport\n"));
|
|
|
|
// clear the suspend flag in case this is a restart
|
|
CLEAR_FLAG(DeviceData->Flags, HMP_FLAG_SUSPENDED);
|
|
// assume success
|
|
mpStatus = USBMP_STATUS_SUCCESS;
|
|
|
|
OHCI_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;
|
|
}
|
|
|
|
// set up or device data structure
|
|
hc = DeviceData->HC = HcResources->DeviceRegisters;
|
|
DeviceData->Sig = SIG_OHCI_DD;
|
|
DeviceData->ControllerFlavor =
|
|
HcResources->ControllerFlavor;
|
|
if (DeviceData->ControllerFlavor == OHCI_Hydra) {
|
|
OHCI_KdPrint((DeviceData, 1, "'OPENHCI Hydra Detected\n"));
|
|
}
|
|
|
|
OHCI_GetRegistryParameters(DeviceData);
|
|
|
|
// init misc fields in the extension
|
|
|
|
// attempt to stop the BIOS
|
|
if (mpStatus == USBMP_STATUS_SUCCESS) {
|
|
mpStatus = OHCI_StopBIOS(DeviceData, HcResources);
|
|
}
|
|
|
|
if (mpStatus == USBMP_STATUS_SUCCESS) {
|
|
|
|
PUCHAR staticEDs;
|
|
HW_32BIT_PHYSICAL_ADDRESS staticEDsPhys;
|
|
|
|
// carve the common buffer block in to HCCA and
|
|
// static EDs
|
|
//
|
|
// set the HCCA and
|
|
// set up the schedule
|
|
|
|
DeviceData->HcHCCA = (PHCCA_BLOCK)
|
|
HcResources->CommonBufferVa;
|
|
DeviceData->HcHCCAPhys =
|
|
HcResources->CommonBufferPhys;
|
|
endCommonBuffer = HcResources->CommonBufferVa +
|
|
OHCI_COMMON_BUFFER_SIZE;
|
|
|
|
|
|
staticEDs = HcResources->CommonBufferVa + sizeof(HCCA_BLOCK);
|
|
staticEDsPhys = HcResources->CommonBufferPhys + sizeof(HCCA_BLOCK);
|
|
mpStatus = OHCI_InitializeSchedule(DeviceData,
|
|
staticEDs,
|
|
staticEDsPhys,
|
|
endCommonBuffer);
|
|
|
|
}
|
|
|
|
if (mpStatus == USBMP_STATUS_SUCCESS) {
|
|
// got resources and schedule
|
|
// init the controller
|
|
mpStatus = OHCI_InitializeHardware(DeviceData);
|
|
}
|
|
|
|
if (mpStatus == USBMP_STATUS_SUCCESS) {
|
|
HC_CONTROL control;
|
|
|
|
// When the HC is in the operational state, HccaPad1 should be set to
|
|
// zero every time the HC updates HccaFrameNumer. Preset HccaPad1 to
|
|
// zero before entering the operational state. OHCI_CheckController()
|
|
// should always find a zero value in HccaPad1 when the HC is in the
|
|
// operational state.
|
|
//
|
|
DeviceData->HcHCCA->HccaPad1 = 0;
|
|
|
|
// activate the controller
|
|
control.ul = READ_REGISTER_ULONG(&hc->HcControl.ul);
|
|
control.HostControllerFunctionalState = HcHCFS_USBOperational;
|
|
// enable control and bulk interrupt and iso we only disable
|
|
// them if we need to remove ED or if the controller
|
|
// is idle.
|
|
control.ControlListEnable = 1;
|
|
control.BulkListEnable = 1;
|
|
control.PeriodicListEnable = 1;
|
|
control.IsochronousEnable = 1;
|
|
|
|
WRITE_REGISTER_ULONG(&hc->HcControl.ul, control.ul);
|
|
|
|
// enable power for the root hub
|
|
// since messing with the 'operatinal state' messes
|
|
// up the root hub we the do the global power set here
|
|
WRITE_REGISTER_ULONG(&hc->HcRhStatus, HcRhS_SetGlobalPower);
|
|
|
|
} else {
|
|
|
|
DEBUG_BREAK(DeviceData);
|
|
}
|
|
|
|
return mpStatus;
|
|
}
|
|
|
|
|
|
VOID
|
|
USBMPFN
|
|
OHCI_DisableInterrupts(
|
|
PDEVICE_DATA DeviceData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hc = NULL;
|
|
|
|
// set up or device data structure
|
|
hc = DeviceData->HC;
|
|
|
|
WRITE_REGISTER_ULONG(&hc->HcInterruptDisable,
|
|
HcInt_MasterInterruptEnable);
|
|
}
|
|
|
|
|
|
VOID
|
|
USBMPFN
|
|
OHCI_FlushInterrupts(
|
|
PDEVICE_DATA DeviceData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
//nop
|
|
}
|
|
|
|
VOID
|
|
USBMPFN
|
|
OHCI_EnableInterrupts(
|
|
PDEVICE_DATA DeviceData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hc = NULL;
|
|
|
|
// set up or device data structure
|
|
hc = DeviceData->HC;
|
|
|
|
// activate the controllers interrupt
|
|
WRITE_REGISTER_ULONG(&hc->HcInterruptEnable,
|
|
HcInt_MasterInterruptEnable);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_InsertEndpointInSchedule(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Insert an endpoint into the h/w schedule
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
PHC_STATIC_ED_DATA staticEd;
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
|
|
staticEd = EndpointData->StaticEd;
|
|
ed = EndpointData->HcdEd;
|
|
|
|
//
|
|
// Link endpoint descriptor into HCD tracking queue
|
|
//
|
|
// each static ED stucture conatins a list of real
|
|
// EDs (that are for transfers)
|
|
//
|
|
// the HW list is linear with :
|
|
// TransferHwED->TransferHwED->0
|
|
|
|
if (IsListEmpty(&staticEd->TransferEdList)) {
|
|
|
|
//
|
|
// list is currently empty,
|
|
// link it to the head of the hw queue
|
|
//
|
|
|
|
InsertHeadList(&staticEd->TransferEdList, &ed->SwLink.List);
|
|
if (staticEd->EdFlags & EDFLAG_REGISTER) {
|
|
|
|
// control and bulk EDs are linked thru a hw register
|
|
// in the hc
|
|
//
|
|
// the HW list is linear with :
|
|
// TransferHwED->TransferHwED->0
|
|
//
|
|
// update the hardware register that points to the list head
|
|
|
|
LOGENTRY(DeviceData, G, '_IN1', 0, ed, staticEd);
|
|
// next points to static head
|
|
ed->HwED.NextED = READ_REGISTER_ULONG(staticEd->PhysicalHead);
|
|
// new head is this ed
|
|
WRITE_REGISTER_ULONG(staticEd->PhysicalHead, ed->PhysicalAddress);
|
|
|
|
} else {
|
|
|
|
// for interrupt we have two cases
|
|
//
|
|
// case 1:
|
|
// 32ms interrupt, PhysicalHead is the address of the entry
|
|
// in the HCCA that points to the first 32 ms list
|
|
// (ie &HCCA[n] == physicalHead),
|
|
// so we end up with:
|
|
// HCCA[n]->TransferHwED->TransferHwED->StaticEd(32)->
|
|
//
|
|
// case 2:
|
|
// not 32ms interrupt, PhysicaHead is the address of the
|
|
// NextED entry in the static HwED for the list list,
|
|
// (ie &HwED->nextEd == physicalHead)
|
|
// so we end up with
|
|
// StaticEd->TransferHwED->TransferHwED->NextStaticED
|
|
//
|
|
|
|
|
|
LOGENTRY(DeviceData, G, '_IN2', staticEd->PhysicalHead,
|
|
ed, staticEd);
|
|
// tail points to old list head HW ed head
|
|
ed->HwED.NextED = *staticEd->PhysicalHead;
|
|
// new head is this ed
|
|
*staticEd->PhysicalHead = ed->PhysicalAddress;
|
|
}
|
|
} else {
|
|
|
|
PHCD_ENDPOINT_DESCRIPTOR tailEd;
|
|
|
|
//
|
|
// Something already on the list,
|
|
// Link ED into tail of transferEd list
|
|
//
|
|
|
|
tailEd = CONTAINING_RECORD(staticEd->TransferEdList.Blink,
|
|
HCD_ENDPOINT_DESCRIPTOR,
|
|
SwLink);
|
|
|
|
LOGENTRY(DeviceData, G, '_Led', 0, tailEd, staticEd);
|
|
//LOGENTRY(G, 'INT1', list, ed, 0);
|
|
InsertTailList(&staticEd->TransferEdList, &ed->SwLink.List);
|
|
ed->HwED.NextED = 0;
|
|
tailEd->HwED.NextED = ed->PhysicalAddress;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_RemoveEndpointFromSchedule(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove an endpoint from the h/w schedule
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
PHC_STATIC_ED_DATA staticEd;
|
|
PHCD_ENDPOINT_DESCRIPTOR ed, previousEd;
|
|
|
|
staticEd = EndpointData->StaticEd;
|
|
ed = EndpointData->HcdEd;
|
|
OHCI_ASSERT_ED(DeviceData, ed);
|
|
|
|
LOGENTRY(DeviceData, G, '_Red', EndpointData, staticEd, 0);
|
|
|
|
// Unlink the ED from the physical ED list
|
|
|
|
// two cases:
|
|
|
|
if (&staticEd->TransferEdList == ed->SwLink.List.Blink) {
|
|
// case 1, we are at the head of the list
|
|
// make the next guy the head
|
|
LOGENTRY(DeviceData, G, '_yHD', EndpointData, 0, 0);
|
|
if (ed->EdFlags & EDFLAG_REGISTER) {
|
|
WRITE_REGISTER_ULONG(staticEd->PhysicalHead, ed->HwED.NextED);
|
|
} else {
|
|
*staticEd->PhysicalHead = ed->HwED.NextED;
|
|
}
|
|
} else {
|
|
// case 2 we are not at the head
|
|
// use the sw link to get the previus ed
|
|
previousEd =
|
|
CONTAINING_RECORD(ed->SwLink.List.Blink,
|
|
HCD_ENDPOINT_DESCRIPTOR,
|
|
SwLink);
|
|
LOGENTRY(DeviceData, G, '_nHD', EndpointData, previousEd, 0);
|
|
OHCI_ASSERT_ED(DeviceData, previousEd);
|
|
|
|
previousEd->HwED.NextED = ed->HwED.NextED;
|
|
|
|
}
|
|
// remove ourselves from the software list
|
|
RemoveEntryList(&ed->SwLink.List);
|
|
// on no list
|
|
EndpointData->StaticEd = NULL;
|
|
|
|
}
|
|
|
|
|
|
PHCD_ENDPOINT_DESCRIPTOR
|
|
OHCI_InitializeED(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PHCD_ENDPOINT_DESCRIPTOR Ed,
|
|
PHCD_TRANSFER_DESCRIPTOR DummyTd,
|
|
HW_32BIT_PHYSICAL_ADDRESS HwPhysAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize an ED for inserting in to the
|
|
schedule
|
|
|
|
returns a ptr to the ED passed in
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
|
|
RtlZeroMemory(Ed, sizeof(*Ed));
|
|
|
|
Ed->PhysicalAddress = HwPhysAddress;
|
|
ENDPOINT_DATA_PTR(Ed->EndpointData) = EndpointData;
|
|
Ed->Sig = SIG_HCD_ED;
|
|
|
|
// init the hw descriptor
|
|
Ed->HwED.FunctionAddress = EndpointData->Parameters.DeviceAddress;
|
|
Ed->HwED.EndpointNumber = EndpointData->Parameters.EndpointAddress;
|
|
|
|
if (EndpointData->Parameters.TransferType == Control) {
|
|
Ed->HwED.Direction = HcEDDirection_Defer;
|
|
} else if (EndpointData->Parameters.TransferDirection == In) {
|
|
Ed->HwED.Direction = HcEDDirection_In;
|
|
} else {
|
|
Ed->HwED.Direction = HcEDDirection_Out;
|
|
}
|
|
|
|
Ed->HwED.sKip = 1;
|
|
|
|
if (EndpointData->Parameters.DeviceSpeed == LowSpeed) {
|
|
Ed->HwED.LowSpeed = 1;
|
|
}
|
|
|
|
if (EndpointData->Parameters.TransferType == Isochronous) {
|
|
Ed->HwED.Isochronous = 1;
|
|
}
|
|
Ed->HwED.MaxPacket = EndpointData->Parameters.MaxPacketSize;
|
|
|
|
// set head tail ptr to point to the dummy TD
|
|
Ed->HwED.TailP = Ed->HwED.HeadP = DummyTd->PhysicalAddress;
|
|
SET_FLAG(DummyTd->Flags, TD_FLAG_BUSY);
|
|
EndpointData->HcdHeadP = EndpointData->HcdTailP = DummyTd;
|
|
|
|
return Ed;
|
|
}
|
|
|
|
|
|
PHCD_TRANSFER_DESCRIPTOR
|
|
OHCI_InitializeTD(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PHCD_TRANSFER_DESCRIPTOR Td,
|
|
HW_32BIT_PHYSICAL_ADDRESS HwPhysAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize an ED for insertin in to the
|
|
schedule
|
|
|
|
returns a ptr to the ED passed in
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
RtlZeroMemory(Td, sizeof(*Td));
|
|
|
|
Td->PhysicalAddress = HwPhysAddress;
|
|
ENDPOINT_DATA_PTR(Td->EndpointData) = EndpointData;
|
|
Td->Flags = 0;
|
|
Td->Sig = SIG_HCD_TD;
|
|
TRANSFER_CONTEXT_PTR(Td->TransferContext) = FREE_TD_CONTEXT;
|
|
|
|
return Td;
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
OHCI_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;
|
|
InitializeListHead(&EndpointData->DoneTdList);
|
|
|
|
switch (EndpointParameters->TransferType) {
|
|
|
|
case Control:
|
|
mpStatus = OHCI_OpenControlEndpoint(
|
|
DeviceData,
|
|
EndpointParameters,
|
|
EndpointData);
|
|
|
|
break;
|
|
|
|
case Interrupt:
|
|
mpStatus = OHCI_OpenInterruptEndpoint(
|
|
DeviceData,
|
|
EndpointParameters,
|
|
EndpointData);
|
|
|
|
break;
|
|
case Bulk:
|
|
mpStatus = OHCI_OpenBulkEndpoint(
|
|
DeviceData,
|
|
EndpointParameters,
|
|
EndpointData);
|
|
|
|
break;
|
|
case Isochronous:
|
|
mpStatus = OHCI_OpenIsoEndpoint(
|
|
DeviceData,
|
|
EndpointParameters,
|
|
EndpointData);
|
|
|
|
break;
|
|
|
|
default:
|
|
mpStatus = USBMP_STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
return mpStatus;
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
OHCI_PokeEndpoint(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_PARAMETERS EndpointParameters,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
ULONG oldBandwidth;
|
|
|
|
LOGENTRY(DeviceData, G, '_Pok', EndpointData,
|
|
EndpointParameters, 0);
|
|
|
|
ed = EndpointData->HcdEd;
|
|
|
|
oldBandwidth = EndpointData->Parameters.Bandwidth;
|
|
EndpointData->Parameters = *EndpointParameters;
|
|
|
|
ed->HwED.FunctionAddress =
|
|
EndpointData->Parameters.DeviceAddress;
|
|
|
|
ed->HwED.MaxPacket =
|
|
EndpointData->Parameters.MaxPacketSize;
|
|
|
|
// adjust bw if necessary
|
|
if (EndpointData->Parameters.TransferType == Isochronous ||
|
|
EndpointData->Parameters.TransferType == Interrupt) {
|
|
|
|
// subtract the old bandwidth
|
|
EndpointData->StaticEd->AllocatedBandwidth -=
|
|
oldBandwidth;
|
|
// add on new bw
|
|
EndpointData->StaticEd->AllocatedBandwidth +=
|
|
EndpointData->Parameters.Bandwidth;
|
|
}
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_QueryEndpointRequirements(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_PARAMETERS EndpointParameters,
|
|
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_ENDPOINT_DESCRIPTOR) +
|
|
TDS_PER_CONTROL_ENDPOINT*sizeof(HCD_TRANSFER_DESCRIPTOR);
|
|
|
|
EndpointRequirements->MaximumTransferSize =
|
|
MAX_CONTROL_TRANSFER_SIZE;
|
|
|
|
break;
|
|
|
|
case Interrupt:
|
|
|
|
EndpointRequirements->MinCommonBufferBytes =
|
|
sizeof(HCD_ENDPOINT_DESCRIPTOR) +
|
|
TDS_PER_INTERRUPT_ENDPOINT*sizeof(HCD_TRANSFER_DESCRIPTOR);
|
|
|
|
#ifdef TEST_SPLIT
|
|
EndpointRequirements->MaximumTransferSize =
|
|
EndpointParameters->MaxPacketSize;
|
|
|
|
#else
|
|
EndpointRequirements->MaximumTransferSize =
|
|
MAX_INTERRUPT_TRANSFER_SIZE;
|
|
#endif
|
|
break;
|
|
|
|
case Bulk:
|
|
|
|
EndpointRequirements->MinCommonBufferBytes =
|
|
sizeof(HCD_ENDPOINT_DESCRIPTOR) +
|
|
TDS_PER_BULK_ENDPOINT*sizeof(HCD_TRANSFER_DESCRIPTOR);
|
|
#ifdef TEST_SPLIT
|
|
EndpointRequirements->MaximumTransferSize = 4096;
|
|
|
|
#else
|
|
EndpointRequirements->MaximumTransferSize =
|
|
MAX_BULK_TRANSFER_SIZE;
|
|
#endif
|
|
break;
|
|
|
|
case Isochronous:
|
|
|
|
// BUGBUG NOTE
|
|
// the 1.1 USBDI caped requests at 255 packets per urb
|
|
EndpointRequirements->MinCommonBufferBytes =
|
|
sizeof(HCD_ENDPOINT_DESCRIPTOR) +
|
|
TDS_PER_ISO_ENDPOINT*sizeof(HCD_TRANSFER_DESCRIPTOR);
|
|
|
|
EndpointRequirements->MaximumTransferSize =
|
|
MAX_ISO_TRANSFER_SIZE;
|
|
|
|
break;
|
|
|
|
default:
|
|
TEST_TRAP();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_CloseEndpoint(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
// nothing to do here
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_PollEndpoint(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
switch(EndpointData->Parameters.TransferType) {
|
|
case Control:
|
|
case Interrupt:
|
|
case Bulk:
|
|
OHCI_PollAsyncEndpoint(DeviceData, EndpointData);
|
|
break;
|
|
case Isochronous:
|
|
OHCI_PollIsoEndpoint(DeviceData, EndpointData);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_PollController(
|
|
PDEVICE_DATA DeviceData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN hcOk = TRUE;
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
ULONG irqStatus;
|
|
|
|
hc = DeviceData->HC;
|
|
|
|
if (TEST_FLAG(DeviceData->Flags, HMP_FLAG_SUSPENDED)) {
|
|
|
|
ULONG ps;
|
|
ULONG p;
|
|
|
|
// indicates polling while controller is
|
|
// suspended but in D0
|
|
//
|
|
// should invalidate the root hub only if changes
|
|
// are detected on the root ports.
|
|
|
|
//
|
|
// check all the ports
|
|
//
|
|
|
|
for (p=0; p< DeviceData->NumberOfPorts; p++) {
|
|
ps = READ_REGISTER_ULONG(&hc->HcRhPortStatus[p]);
|
|
|
|
if (ps & HcRhPS_ConnectStatusChange) {
|
|
//TEST_TRAP();
|
|
LOGENTRY(DeviceData, G, '_rPS', DeviceData, 0, 0);
|
|
USBPORT_INVALIDATE_ROOTHUB(DeviceData);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
// see if the controller is still operating
|
|
|
|
fn = DeviceData->HcHCCA->HccaFrameNumber;
|
|
if (DeviceData->LastFn && DeviceData->LastFn == fn) {
|
|
hcOk = FALSE;
|
|
}
|
|
|
|
if (hcOK) {
|
|
DeviceData->LastFn = fn;
|
|
} else {
|
|
OHCI_KdPrint((DeviceData, 0, "Controller has crashed\n");
|
|
// bugbug, signal USBPORT to attempt recovery
|
|
TEST_TRAP();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_AbortTransfer(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PTRANSFER_CONTEXT AbortTransferContext,
|
|
PULONG BytesTransferred
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when a transfer needs to be removed
|
|
from the schedule.
|
|
|
|
This process is vertually identical regardless
|
|
of transfer type
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PHCD_TRANSFER_DESCRIPTOR td, currentTd, tmpTd;
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
ULONG i;
|
|
BOOLEAN found = FALSE;
|
|
BOOLEAN iso = FALSE; // assume its async
|
|
|
|
if (EndpointData->Parameters.TransferType == Isochronous) {
|
|
iso = TRUE;
|
|
}
|
|
|
|
ed = EndpointData->HcdEd;
|
|
|
|
//
|
|
// The endpoint should have the skip bit set
|
|
// ie 'paused'
|
|
//
|
|
OHCI_ASSERT(DeviceData, ed->HwED.sKip == 1);
|
|
|
|
LOGENTRY(DeviceData, G, '_abr', ed, AbortTransferContext,
|
|
EndpointData);
|
|
|
|
// one less pending transfer
|
|
EndpointData->PendingTransfers--;
|
|
|
|
// our mission now is to remove all TDs associated with
|
|
// this transfer
|
|
|
|
// get the 'currentTD'
|
|
currentTd = (PHCD_TRANSFER_DESCRIPTOR)
|
|
USBPORT_PHYSICAL_TO_VIRTUAL(ed->HwED.HeadP & ~HcEDHeadP_FLAGS,
|
|
DeviceData,
|
|
EndpointData);
|
|
|
|
// we have three possible cases to deal with:
|
|
// case 1: the transfer is current, headp points to a TD
|
|
// associated with this transfer
|
|
// case 2: transfer is already done, we just need to free
|
|
// the TDs
|
|
// case 3: transfer is not processed, we need to link
|
|
// the current transfer to the next.
|
|
|
|
|
|
if (TRANSFER_CONTEXT_PTR(currentTd->TransferContext)
|
|
== AbortTransferContext) {
|
|
|
|
LOGENTRY(DeviceData, G, '_aCU', currentTd,
|
|
0, 0);
|
|
|
|
// case 1: transfer is current
|
|
|
|
found = TRUE;
|
|
|
|
// set Headp to next transfer and update sw pointers in ED
|
|
tmpTd = AbortTransferContext->NextXferTd;
|
|
// preserve the data toggle for whatever the transfer
|
|
ed->HwED.HeadP = tmpTd->PhysicalAddress |
|
|
(ed->HwED.HeadP & HcEDHeadP_CARRY);
|
|
EndpointData->HcdHeadP = tmpTd;
|
|
|
|
// loop thru all TDs and free the ones for this tarnsfer
|
|
for (i=0; i<EndpointData->TdCount; i++) {
|
|
tmpTd = &EndpointData->TdList->Td[i];
|
|
|
|
if (TRANSFER_CONTEXT_PTR(tmpTd->TransferContext)
|
|
== AbortTransferContext) {
|
|
if (iso) {
|
|
OHCI_ProcessDoneIsoTd(DeviceData,
|
|
tmpTd,
|
|
FALSE);
|
|
} else {
|
|
OHCI_ProcessDoneAsyncTd(DeviceData,
|
|
tmpTd,
|
|
FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// not current, walk the the list of TDs from the
|
|
// last known HeadP to the current TD if we find it
|
|
// it is already done (case 2).
|
|
|
|
// Issue to investigate: What if we find some TDs which belong to
|
|
// this transfer but we stop walking the TD list when we hit currentTd
|
|
// and there are still TDs queued which belong to this transfer? If
|
|
// they stay stuck on the HW and the transfer is freed that would be
|
|
// bad.
|
|
|
|
td = EndpointData->HcdHeadP;
|
|
while (td != currentTd) {
|
|
|
|
PTRANSFER_CONTEXT transfer;
|
|
|
|
transfer = TRANSFER_CONTEXT_PTR(td->TransferContext);
|
|
ASSERT_TRANSFER(DeviceData, transfer);
|
|
|
|
if (transfer == AbortTransferContext) {
|
|
// case 2 the transfer TDs have already
|
|
// been comlpleted by the hardware
|
|
found = TRUE;
|
|
|
|
LOGENTRY(DeviceData, G, '_aDN', currentTd,
|
|
td, 0);
|
|
|
|
// free this TD
|
|
tmpTd = td;
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
|
|
// if this TD was the head we need to bump the
|
|
// headp
|
|
if (tmpTd == EndpointData->HcdHeadP) {
|
|
EndpointData->HcdHeadP = td;
|
|
}
|
|
|
|
if (iso) {
|
|
OHCI_ProcessDoneIsoTd(DeviceData,
|
|
tmpTd,
|
|
FALSE);
|
|
} else {
|
|
OHCI_ProcessDoneAsyncTd(DeviceData,
|
|
tmpTd,
|
|
FALSE);
|
|
}
|
|
|
|
} else {
|
|
|
|
// we walk the SW links
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
PHCD_TRANSFER_DESCRIPTOR firstTd, lastTd;
|
|
PTRANSFER_CONTEXT prevTransfer;
|
|
|
|
// case 3 the transfer is not current and not done.
|
|
// 1. we need to find it.
|
|
// 2. unlink it from the prevoius transfer
|
|
// 3. free the TDs
|
|
// 4. link prev transfer to the next
|
|
|
|
|
|
|
|
td = EndpointData->HcdHeadP;
|
|
firstTd = NULL;
|
|
|
|
LOGENTRY(DeviceData, G, '_abP', EndpointData->HcdHeadP,
|
|
EndpointData->HcdTailP, currentTd);
|
|
|
|
// start at the current HeadP and find the first
|
|
// td for this transfer
|
|
|
|
lastTd = td;
|
|
while (td != EndpointData->HcdTailP) {
|
|
PTRANSFER_CONTEXT transfer;
|
|
|
|
transfer = TRANSFER_CONTEXT_PTR(td->TransferContext);
|
|
ASSERT_TRANSFER(DeviceData, transfer);
|
|
|
|
if (transfer == AbortTransferContext) {
|
|
// found it
|
|
LOGENTRY(DeviceData, G, '_fnT', transfer,
|
|
td, 0);
|
|
|
|
firstTd = td;
|
|
break;
|
|
} else {
|
|
lastTd = td;
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
}
|
|
}
|
|
|
|
OHCI_ASSERT(DeviceData, firstTd != NULL);
|
|
|
|
// found the first TD, walk to the HcdTailP or the
|
|
// next transfer and free these TDs
|
|
td = firstTd;
|
|
while (td != EndpointData->HcdTailP) {
|
|
|
|
tmpTd = td;
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
|
|
if (iso) {
|
|
OHCI_ProcessDoneIsoTd(DeviceData,
|
|
tmpTd,
|
|
FALSE);
|
|
} else {
|
|
OHCI_ProcessDoneAsyncTd(DeviceData,
|
|
tmpTd,
|
|
FALSE);
|
|
}
|
|
|
|
if (TRANSFER_CONTEXT_PTR(td->TransferContext) !=
|
|
AbortTransferContext) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
LOGENTRY(DeviceData, G, '_NnT', 0, td, 0);
|
|
|
|
// td should now point to the next Transfer (or the
|
|
// tail)
|
|
|
|
OHCI_ASSERT(DeviceData,
|
|
TRANSFER_CONTEXT_PTR(td->TransferContext) !=
|
|
AbortTransferContext);
|
|
|
|
// BUGBUG toggle?
|
|
|
|
// link last TD of the prev transfer to this TD
|
|
//
|
|
prevTransfer = TRANSFER_CONTEXT_PTR(lastTd->TransferContext);
|
|
|
|
prevTransfer->NextXferTd = td;
|
|
|
|
TRANSFER_DESCRIPTOR_PTR(lastTd->NextHcdTD) = td;
|
|
|
|
lastTd->HwTD.NextTD = td->PhysicalAddress;
|
|
}
|
|
|
|
*BytesTransferred = AbortTransferContext->BytesTransferred;
|
|
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
OHCI_SubmitIsoTransfer(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PTRANSFER_PARAMETERS TransferParameters,
|
|
PTRANSFER_CONTEXT TransferContext,
|
|
PMINIPORT_ISO_TRANSFER IsoTransfer
|
|
)
|
|
{
|
|
USB_MINIPORT_STATUS mpStatus;
|
|
|
|
// init the context
|
|
RtlZeroMemory(TransferContext, sizeof(*TransferContext));
|
|
TransferContext->Sig = SIG_OHCI_TRANSFER;
|
|
TransferContext->UsbdStatus = USBD_STATUS_SUCCESS;
|
|
TransferContext->EndpointData = EndpointData;
|
|
TransferContext->TransferParameters = TransferParameters;
|
|
|
|
OHCI_ASSERT(DeviceData,
|
|
EndpointData->Parameters.TransferType == Isochronous);
|
|
|
|
mpStatus =
|
|
OHCI_IsoTransfer(DeviceData,
|
|
EndpointData,
|
|
TransferParameters,
|
|
TransferContext,
|
|
IsoTransfer);
|
|
|
|
return mpStatus;
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
OHCI_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_OHCI_TRANSFER;
|
|
TransferContext->UsbdStatus = USBD_STATUS_SUCCESS;
|
|
TransferContext->EndpointData = EndpointData;
|
|
TransferContext->TransferParameters = TransferParameters;
|
|
|
|
switch (EndpointData->Parameters.TransferType) {
|
|
case Control:
|
|
mpStatus =
|
|
OHCI_ControlTransfer(DeviceData,
|
|
EndpointData,
|
|
TransferParameters,
|
|
TransferContext,
|
|
TransferSGList);
|
|
break;
|
|
case Interrupt:
|
|
case Bulk:
|
|
mpStatus =
|
|
OHCI_BulkOrInterruptTransfer(DeviceData,
|
|
EndpointData,
|
|
TransferParameters,
|
|
TransferContext,
|
|
TransferSGList);
|
|
break;
|
|
default:
|
|
TEST_TRAP();
|
|
}
|
|
|
|
return mpStatus;
|
|
}
|
|
|
|
|
|
PHCD_TRANSFER_DESCRIPTOR
|
|
OHCI_AllocTd(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate a TD, it is OK to fail
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
PHCD_TRANSFER_DESCRIPTOR td;
|
|
|
|
for (i=0; i<EndpointData->TdCount; i++) {
|
|
td = &EndpointData->TdList->Td[i];
|
|
|
|
if (!(td->Flags & TD_FLAG_BUSY)) {
|
|
SET_FLAG(td->Flags, TD_FLAG_BUSY);
|
|
LOGENTRY(DeviceData, G, '_aTD', td, 0, 0);
|
|
OHCI_ASSERT(DeviceData, td->DoneLink.Flink == NULL &&
|
|
td->DoneLink.Blink == NULL);
|
|
return td;
|
|
}
|
|
}
|
|
|
|
// if we don't have enough TDs the caller has to handle this case.
|
|
// generally we make sure we have enough before we ever call this
|
|
// function so the callers just assert that the return is not
|
|
// USB_BAD_PTR
|
|
|
|
return USB_BAD_PTR;
|
|
}
|
|
|
|
|
|
ULONG
|
|
OHCI_FreeTds(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
return the number of free TDs
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
PHCD_TRANSFER_DESCRIPTOR td;
|
|
ULONG n=0;
|
|
|
|
for (i=0; i<EndpointData->TdCount; i++) {
|
|
td = &EndpointData->TdList->Td[i];
|
|
|
|
if (!(td->Flags & TD_FLAG_BUSY)) {
|
|
n++;
|
|
}
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_EnableList(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
ULONG listFilled = 0;
|
|
ULONG temp;
|
|
|
|
hc = DeviceData->HC;
|
|
|
|
temp = READ_REGISTER_ULONG(&hc->HcControlHeadED);
|
|
if (temp) {
|
|
SET_FLAG(listFilled, HcCmd_ControlListFilled);
|
|
}
|
|
|
|
temp = READ_REGISTER_ULONG (&hc->HcBulkHeadED);
|
|
if (temp) {
|
|
SET_FLAG(listFilled, HcCmd_BulkListFilled);
|
|
}
|
|
|
|
if (EndpointData->Parameters.TransferType == Bulk) {
|
|
SET_FLAG(listFilled, HcCmd_BulkListFilled);
|
|
} else if (EndpointData->Parameters.TransferType == Control) {
|
|
SET_FLAG(listFilled, HcCmd_ControlListFilled);
|
|
}
|
|
|
|
WRITE_REGISTER_ULONG(&hc->HcCommandStatus.ul,
|
|
listFilled);
|
|
|
|
LOGENTRY(DeviceData, G, '_enL', listFilled, EndpointData, 0);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_SetEndpointStatus(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
MP_ENDPOINT_STATUS Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
|
|
ed = EndpointData->HcdEd;
|
|
|
|
switch(Status) {
|
|
case ENDPOINT_STATUS_RUN:
|
|
// clear halt bit
|
|
ed->HwED.HeadP &= ~HcEDHeadP_HALT;
|
|
OHCI_EnableList(DeviceData, EndpointData);
|
|
break;
|
|
|
|
case ENDPOINT_STATUS_HALT:
|
|
TEST_TRAP();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
MP_ENDPOINT_STATUS
|
|
OHCI_GetEndpointStatus(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
MP_ENDPOINT_STATUS status = ENDPOINT_STATUS_RUN;
|
|
|
|
ed = EndpointData->HcdEd;
|
|
|
|
if ((ed->HwED.HeadP & HcEDHeadP_HALT) &&
|
|
!TEST_FLAG(ed->EdFlags, EDFLAG_NOHALT)) {
|
|
status = ENDPOINT_STATUS_HALT;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_SetEndpointState(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
MP_ENDPOINT_STATE State
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
|
|
ed = EndpointData->HcdEd;
|
|
|
|
switch(State) {
|
|
case ENDPOINT_ACTIVE:
|
|
// clear the skip bit
|
|
ed->HwED.sKip = 0;
|
|
// if its bulk or control set the
|
|
// 'list filled' bits
|
|
OHCI_EnableList(DeviceData, EndpointData);
|
|
break;
|
|
|
|
case ENDPOINT_PAUSE:
|
|
ed->HwED.sKip = 1;
|
|
break;
|
|
|
|
case ENDPOINT_REMOVE:
|
|
|
|
SET_FLAG(ed->EdFlags, EDFLAG_REMOVED);
|
|
ed->HwED.sKip = 1;
|
|
// free the bw
|
|
EndpointData->StaticEd->AllocatedBandwidth -=
|
|
EndpointData->Parameters.Bandwidth;
|
|
|
|
OHCI_RemoveEndpointFromSchedule(DeviceData,
|
|
EndpointData);
|
|
|
|
break;
|
|
|
|
default:
|
|
TEST_TRAP();
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_SetEndpointDataToggle(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
ULONG Toggle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Toggle is 0 or 1
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
|
|
ed = EndpointData->HcdEd;
|
|
|
|
if (Toggle == 0) {
|
|
ed->HwED.HeadP &= ~HcEDHeadP_CARRY;
|
|
} else {
|
|
ed->HwED.HeadP |= HcEDHeadP_CARRY;
|
|
}
|
|
|
|
// we should get here unless we are paused or halted or
|
|
// we have no tranfsers
|
|
OHCI_ASSERT(DeviceData, (ed->HwED.sKip == 1) ||
|
|
(ed->HwED.HeadP & HcEDHeadP_HALT) ||
|
|
((ed->HwED.HeadP & ~HcEDHeadP_FLAGS) == ed->HwED.TailP));
|
|
|
|
LOGENTRY(DeviceData, G, '_stg', EndpointData, 0, Toggle);
|
|
}
|
|
|
|
|
|
MP_ENDPOINT_STATE
|
|
OHCI_GetEndpointState(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
MP_ENDPOINT_STATE state = ENDPOINT_ACTIVE;
|
|
|
|
ed = EndpointData->HcdEd;
|
|
|
|
if (TEST_FLAG(ed->EdFlags, EDFLAG_REMOVED)) {
|
|
state = ENDPOINT_REMOVE;
|
|
goto OHCI_GetEndpointState_Done;
|
|
}
|
|
|
|
if (ed->HwED.sKip == 1) {
|
|
state = ENDPOINT_PAUSE;
|
|
goto OHCI_GetEndpointState_Done;
|
|
}
|
|
|
|
OHCI_GetEndpointState_Done:
|
|
|
|
LOGENTRY(DeviceData, G, '_eps', EndpointData, 0, state);
|
|
|
|
return state;
|
|
}
|
|
|
|
|
|
#if 0
|
|
VOID
|
|
USBMPFN
|
|
OHCI_SendGoatPacket(
|
|
PDEVICE_DATA DeviceData,
|
|
PUCHAR WorkspaceVirtualAddress,
|
|
HW_32BIT_PHYSICAL_ADDRESS WorkspacePhysicalAddress
|
|
COMPLETION ROUTINE
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Transmit the 'magic' iso packet.
|
|
|
|
This is a fire and forget API so
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
PHW_TRANSFER_DESCRIPTOR hwTD;
|
|
|
|
// hang a special ISO td of the static is ED
|
|
pch = WorkspaceVirtualAddress;
|
|
phys = WorkspacePhysicalAddress;
|
|
|
|
hwTD = pch;
|
|
hwTDPhys = phys;
|
|
|
|
pch += xxx;
|
|
phys += xxx
|
|
|
|
goatData = pch;
|
|
goatDataPhys = phys;
|
|
|
|
pch += sizeof(USB_GOAT_DATA);
|
|
phys += sizeof(USB_GOAT_DATA);
|
|
|
|
// initialize the goat packet
|
|
|
|
strcpy(goatData, USB_GOAT_DATA,
|
|
|
|
hwTD->NextTD = 0;
|
|
hwTD->CBP = goatDataPhys;
|
|
hwTD->BE = dataPhys+sizeof(USB_GOAT_DATA)-1;
|
|
hwTD->ConditionCode = HcCC_NotAccessed;
|
|
hwTD->ErrorCount = 0;
|
|
hwTD->IntDelay = HcTDIntDelay_0ms;
|
|
hwTD->ShortXferOk = 0;
|
|
|
|
hwTD->Isochrinous = 1;
|
|
hwTD->FrameCount = 0;
|
|
hwTD->StartFrameNumber = xxx;
|
|
|
|
// hang the TD on the static ISO ED
|
|
|
|
// clear the skip bit
|
|
}
|
|
#endif
|
|
|
|
USB_MINIPORT_STATUS
|
|
USBMPFN
|
|
OHCI_StartSendOnePacket(
|
|
PDEVICE_DATA DeviceData,
|
|
PMP_PACKET_PARAMETERS PacketParameters,
|
|
PUCHAR PacketData,
|
|
PULONG PacketLength,
|
|
PUCHAR WorkspaceVirtualAddress,
|
|
HW_32BIT_PHYSICAL_ADDRESS WorkspacePhysicalAddress,
|
|
ULONG WorkSpaceLength,
|
|
USBD_STATUS *UsbdStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
insert structures to transmit a single packet -- this is for debug
|
|
tool purposes only so we can be a little creative here.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
PUCHAR pch;
|
|
PHW_ENDPOINT_DESCRIPTOR hwED;
|
|
ULONG hwEDPhys, phys;
|
|
PHW_TRANSFER_DESCRIPTOR hwTD, hwDummyTD;
|
|
ULONG hwTDPhys, hwDummyTDPhys, dataPhys;
|
|
PUCHAR data;
|
|
PHC_STATIC_ED_DATA staticControlEd;
|
|
ULONG i;
|
|
PSS_PACKET_CONTEXT context;
|
|
|
|
staticControlEd = &DeviceData->StaticEDList[ED_CONTROL];
|
|
hc = DeviceData->HC;
|
|
|
|
// allocate an ED & TD from the scratch space and initialize it
|
|
phys = WorkspacePhysicalAddress;
|
|
pch = WorkspaceVirtualAddress;
|
|
|
|
LOGENTRY(DeviceData, G, '_ssS', phys, 0, pch);
|
|
|
|
context = (PSS_PACKET_CONTEXT) pch;
|
|
pch += sizeof(SS_PACKET_CONTEXT);
|
|
phys += sizeof(SS_PACKET_CONTEXT);
|
|
|
|
|
|
// carve out an ED
|
|
hwEDPhys = phys;
|
|
hwED = (PHW_ENDPOINT_DESCRIPTOR) pch;
|
|
pch += sizeof(HW_ENDPOINT_DESCRIPTOR);
|
|
phys += sizeof(HW_ENDPOINT_DESCRIPTOR);
|
|
|
|
// carve out a TD
|
|
hwTDPhys = phys;
|
|
hwTD = (PHW_TRANSFER_DESCRIPTOR) pch;
|
|
pch += sizeof(HW_TRANSFER_DESCRIPTOR);
|
|
phys += sizeof(HW_TRANSFER_DESCRIPTOR);
|
|
|
|
// carve out a dummy TD
|
|
hwDummyTDPhys = phys;
|
|
hwDummyTD = (PHW_TRANSFER_DESCRIPTOR) pch;
|
|
pch += sizeof(HW_TRANSFER_DESCRIPTOR);
|
|
phys += sizeof(HW_TRANSFER_DESCRIPTOR);
|
|
|
|
// use the rest for data
|
|
LOGENTRY(DeviceData, G, '_ssD', PacketData, *PacketLength, 0);
|
|
|
|
dataPhys = phys;
|
|
data = pch;
|
|
RtlCopyMemory(data, PacketData, *PacketLength);
|
|
pch+=*PacketLength;
|
|
phys+=*PacketLength;
|
|
|
|
// init the hw ed descriptor
|
|
hwED->NextED = 0;
|
|
hwED->FunctionAddress = PacketParameters->DeviceAddress;
|
|
hwED->EndpointNumber = PacketParameters->EndpointAddress;
|
|
hwED->sKip = 0;
|
|
hwED->Direction = HcEDDirection_Defer;
|
|
switch (PacketParameters->Speed) {
|
|
case ss_Low:
|
|
hwED->LowSpeed = 1;
|
|
break;
|
|
default:
|
|
hwED->LowSpeed = 0;
|
|
}
|
|
hwED->MaxPacket = PacketParameters->MaximumPacketSize;
|
|
hwED->HeadP = hwTDPhys;
|
|
hwED->TailP = hwDummyTDPhys;
|
|
|
|
// init the TD for this packet
|
|
hwTD->NextTD = hwDummyTDPhys;
|
|
hwTD->Asy.ConditionCode = HcCC_NotAccessed;
|
|
hwTD->Asy.ErrorCount = 0;
|
|
hwTD->Asy.IntDelay = HcTDIntDelay_0ms;
|
|
hwTD->Asy.ShortXferOk = 1;
|
|
|
|
if (0 == *PacketLength) {
|
|
hwTD->CBP = 0;
|
|
hwTD->BE = 0;
|
|
}
|
|
else {
|
|
hwTD->CBP = dataPhys;
|
|
hwTD->BE = dataPhys+*PacketLength-1;
|
|
}
|
|
|
|
// init the dummy TD
|
|
hwDummyTD->NextTD = 0;
|
|
hwDummyTD->CBP = 0xFFFFFFFF;
|
|
|
|
LOGENTRY(DeviceData, G, '_ss2', hwTD, context, hwED);
|
|
LOGENTRY(DeviceData, G, '_ss3', dataPhys, data, *PacketLength);
|
|
|
|
switch(PacketParameters->Type) {
|
|
case ss_Setup:
|
|
LOGENTRY(DeviceData, G, '_sSU', 0, 0, 0);
|
|
hwED->Direction = HcEDDirection_Defer;
|
|
hwED->Isochronous = 0;
|
|
hwTD->Asy.Direction = HcTDDirection_Setup;
|
|
hwTD->Asy.Isochronous = 0;
|
|
break;
|
|
case ss_In:
|
|
LOGENTRY(DeviceData, G, '_ssI', 0, 0, 0);
|
|
hwED->Direction = HcEDDirection_Defer;
|
|
hwED->Isochronous = 0;
|
|
hwTD->Asy.Direction = HcTDDirection_In;
|
|
hwTD->Asy.Isochronous = 0;
|
|
break;
|
|
case ss_Out:
|
|
LOGENTRY(DeviceData, G, '_ssO', 0, 0, 0);
|
|
hwED->Direction = HcEDDirection_Defer;
|
|
hwED->Isochronous = 0;
|
|
hwTD->Asy.Direction = HcTDDirection_Out;
|
|
hwTD->Asy.Isochronous = 0;
|
|
break;
|
|
case ss_Iso_In:
|
|
break;
|
|
case ss_Iso_Out:
|
|
break;
|
|
}
|
|
|
|
switch(PacketParameters->Toggle) {
|
|
case ss_Toggle0:
|
|
hwTD->Asy.Toggle = HcTDToggle_Data0;
|
|
break;
|
|
case ss_Toggle1:
|
|
hwTD->Asy.Toggle = HcTDToggle_Data1;
|
|
break;
|
|
}
|
|
|
|
//TEST_TRAP();
|
|
|
|
//
|
|
// Replace the control ED in the list with the ED just created.
|
|
// Save the old value of both the control and bulk lists so
|
|
// they can be replaced when this transfer is complete.
|
|
//
|
|
// NOTE: This will interrupt normal bus operation for at least one ms
|
|
|
|
context->PhysHold = READ_REGISTER_ULONG(staticControlEd->PhysicalHead);
|
|
HW_DATA_PTR(context->Data) = data;
|
|
HW_TRANSFER_DESCRIPTOR_PTR(context->Td) = hwTD;
|
|
|
|
WRITE_REGISTER_ULONG(staticControlEd->PhysicalHead, hwEDPhys);
|
|
|
|
//
|
|
// Enable the control list and disable the bulk list. Disabling the
|
|
// bulk list temporarily will allow the single step transaction to
|
|
// complete without interfering with bulk data. In this manner, the
|
|
// bulk data INs and OUTs can be sent without interfering with bulk
|
|
// devices currently on the bus.
|
|
//
|
|
// NOTE: I think attempting to use this feature without first disabling
|
|
// the root hub could lead to some problems.
|
|
//
|
|
|
|
WRITE_REGISTER_ULONG(&hc->HcCommandStatus.ul, HcCmd_ControlListFilled);
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
USBMPFN
|
|
OHCI_EndSendOnePacket(
|
|
PDEVICE_DATA DeviceData,
|
|
PMP_PACKET_PARAMETERS PacketParameters,
|
|
PUCHAR PacketData,
|
|
PULONG PacketLength,
|
|
PUCHAR WorkspaceVirtualAddress,
|
|
HW_32BIT_PHYSICAL_ADDRESS WorkspacePhysicalAddress,
|
|
ULONG WorkSpaceLength,
|
|
USBD_STATUS *UsbdStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
PUCHAR pch;
|
|
PSS_PACKET_CONTEXT context;
|
|
PHC_STATIC_ED_DATA staticControlEd;
|
|
PHC_STATIC_ED_DATA staticBulkEd;
|
|
ULONG currentBulkEd;
|
|
|
|
PHW_TRANSFER_DESCRIPTOR hwTd;
|
|
PUCHAR data;
|
|
ULONG listFilled;
|
|
|
|
staticControlEd = &DeviceData->StaticEDList[ED_CONTROL];
|
|
staticBulkEd = &DeviceData->StaticEDList[ED_BULK];
|
|
|
|
hc = DeviceData->HC;
|
|
context = (PSS_PACKET_CONTEXT) WorkspaceVirtualAddress;
|
|
hwTd = HW_TRANSFER_DESCRIPTOR_PTR(context->Td);
|
|
data = HW_DATA_PTR(context->Data);
|
|
|
|
LOGENTRY(DeviceData, G, '_ssE', hwTd, 0, 0);
|
|
|
|
//TEST_TRAP();
|
|
|
|
// compute bytes transferred
|
|
if (hwTd->CBP) {
|
|
// we never have pagebreaks in the single step TD
|
|
*PacketLength = *PacketLength - ((hwTd->BE & OHCI_PAGE_SIZE_MASK) -
|
|
(hwTd->CBP & OHCI_PAGE_SIZE_MASK)+1);
|
|
}
|
|
|
|
// return any errors
|
|
if (hwTd->Asy.ConditionCode == HcCC_NoError) {
|
|
*UsbdStatus = USBD_STATUS_SUCCESS;
|
|
} else {
|
|
*UsbdStatus =
|
|
(hwTd->Asy.ConditionCode | 0xC0000000);
|
|
}
|
|
|
|
LOGENTRY(DeviceData, G, '_ssX', hwTd, *PacketLength, 0);
|
|
|
|
RtlCopyMemory(PacketData,
|
|
data,
|
|
*PacketLength);
|
|
|
|
//
|
|
// Restore the previous control structure and enable the control and
|
|
// bulk lists if they are non-NULL (ie. point to valid EDs.)
|
|
//
|
|
|
|
listFilled = 0;
|
|
|
|
WRITE_REGISTER_ULONG(staticControlEd->PhysicalHead, context->PhysHold);
|
|
if (context->PhysHold) {
|
|
listFilled |= HcCmd_ControlListFilled;
|
|
}
|
|
|
|
currentBulkEd = READ_REGISTER_ULONG(staticBulkEd->PhysicalHead);
|
|
if (currentBulkEd) {
|
|
listFilled |= HcCmd_BulkListFilled;
|
|
}
|
|
|
|
WRITE_REGISTER_ULONG(&hc->HcCommandStatus.ul, listFilled);
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
OHCI_SuspendController(
|
|
PDEVICE_DATA DeviceData
|
|
)
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
HC_CONTROL control;
|
|
|
|
hc = DeviceData->HC;
|
|
|
|
// mask off interrupts that are not appropriate
|
|
WRITE_REGISTER_ULONG(&hc->HcInterruptDisable, 0xFFFFffff);
|
|
|
|
// flush any rogue status
|
|
WRITE_REGISTER_ULONG(&hc->HcInterruptStatus, 0xFFFFffff);
|
|
|
|
// put the controller in 'suspend'
|
|
|
|
control.ul = READ_REGISTER_ULONG(&hc->HcControl.ul);
|
|
control.HostControllerFunctionalState = HcHCFS_USBSuspend;
|
|
control.RemoteWakeupEnable = 1;
|
|
|
|
WRITE_REGISTER_ULONG(&hc->HcControl.ul, control.ul);
|
|
|
|
|
|
// enable the resume interrupt
|
|
WRITE_REGISTER_ULONG(&hc->HcInterruptEnable,
|
|
HcInt_MasterInterruptEnable |
|
|
HcInt_RootHubStatusChange |
|
|
HcInt_ResumeDetected |
|
|
HcInt_UnrecoverableError);
|
|
|
|
SET_FLAG(DeviceData->Flags, HMP_FLAG_SUSPENDED);
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
OHCI_ResumeController(
|
|
PDEVICE_DATA DeviceData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
reverse what was done in 'suspend'
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
HC_CONTROL control;
|
|
|
|
hc = DeviceData->HC;
|
|
|
|
CLEAR_FLAG(DeviceData->Flags, HMP_FLAG_SUSPENDED);
|
|
// is some cases the BIOS trashes the state of the controller,
|
|
// even though we enter suspend.
|
|
// This is usually platform specific and indicates a broken BIOS
|
|
control.ul = READ_REGISTER_ULONG(&hc->HcControl.ul);
|
|
if (control.HostControllerFunctionalState == HcHCFS_USBReset) {
|
|
|
|
return USBMP_STATUS_HARDWARE_FAILURE;
|
|
|
|
} else {
|
|
|
|
// When the HC is in the operational state, HccaPad1 should be set to
|
|
// zero every time the HC updates HccaFrameNumer. Preset HccaPad1 to
|
|
// zero before entering the operational state. OHCI_CheckController()
|
|
// should always find a zero value in HccaPad1 when the HC is in the
|
|
// operational state.
|
|
//
|
|
DeviceData->HcHCCA->HccaPad1 = 0;
|
|
|
|
// put the controller in 'operational' state
|
|
|
|
control.ul = READ_REGISTER_ULONG(&hc->HcControl.ul);
|
|
control.HostControllerFunctionalState = HcHCFS_USBOperational;
|
|
|
|
WRITE_REGISTER_ULONG(&hc->HcControl.ul, control.ul);
|
|
}
|
|
|
|
// re-enable interrupts
|
|
WRITE_REGISTER_ULONG(&hc->HcInterruptEnable,
|
|
HcInt_OwnershipChange |
|
|
HcInt_SchedulingOverrun |
|
|
HcInt_WritebackDoneHead |
|
|
HcInt_FrameNumberOverflow |
|
|
HcInt_UnrecoverableError);
|
|
|
|
WRITE_REGISTER_ULONG(&hc->HcControl.ul, control.ul);
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_Unload(
|
|
PDRIVER_OBJECT DriverObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to a driver object
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
// provide an unload routine
|
|
|
|
// we do this just to test the port driver
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
OHCI_HardwarePresent(
|
|
PDEVICE_DATA DeviceData,
|
|
BOOLEAN Notify
|
|
)
|
|
{
|
|
ULONG tmp;
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
|
|
hc = DeviceData->HC;
|
|
|
|
tmp = READ_REGISTER_ULONG(&hc->HcCommandStatus.ul);
|
|
|
|
if (tmp == 0xffffffff) {
|
|
if (Notify) {
|
|
USBPORT_INVALIDATE_CONTROLLER(DeviceData, UsbMpControllerRemoved);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_CheckController(
|
|
PDEVICE_DATA DeviceData
|
|
)
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
ULONG currentDeadmanFrame;
|
|
ULONG lastDeadmanFrame;
|
|
ULONG frameDelta;
|
|
|
|
hc = DeviceData->HC;
|
|
|
|
// First make sure it looks like the hardware is still present. (This
|
|
// will call USBPORT_INVALIDATE_CONTROLLER() if it looks like the hardware
|
|
// is no longer present).
|
|
//
|
|
if (!OHCI_HardwarePresent(DeviceData, TRUE)) {
|
|
return;
|
|
}
|
|
|
|
// Don't check any further if the controller is not currently in the
|
|
// operational state.
|
|
//
|
|
if ((READ_REGISTER_ULONG(&hc->HcControl.ul) & HcCtrl_HCFS_MASK) !=
|
|
HcCtrl_HCFS_USBOperational) {
|
|
return;
|
|
}
|
|
|
|
// Don't check any further if we already checked once this frame (or in
|
|
// the last few frames).
|
|
//
|
|
currentDeadmanFrame = READ_REGISTER_ULONG(&hc->HcFmNumber);
|
|
|
|
lastDeadmanFrame = DeviceData->LastDeadmanFrame;
|
|
|
|
frameDelta = (currentDeadmanFrame - lastDeadmanFrame) & HcFmNumber_MASK;
|
|
|
|
// Might HcFmNumber erroneously read back as zero under some conditions
|
|
// on some chipsets? Don't check any further if HcFmNumber is zero,
|
|
// just check later next time around.
|
|
//
|
|
if (currentDeadmanFrame && (frameDelta >= 5)) {
|
|
|
|
DeviceData->LastDeadmanFrame = currentDeadmanFrame;
|
|
|
|
switch (DeviceData->HcHCCA->HccaPad1)
|
|
{
|
|
case 0:
|
|
//
|
|
// When the HC updates HccaFrameNumber, it is supposed
|
|
// to set HccaPad1 to zero, so this is the expected case.
|
|
// Here we set HccaPad1 to a non-zero value to try to
|
|
// detect situations when the HC is no longer functioning
|
|
// correctly and accessing and updating host memory.
|
|
//
|
|
DeviceData->HcHCCA->HccaPad1 = 0xBAD1;
|
|
|
|
break;
|
|
|
|
case 0xBAD1:
|
|
//
|
|
// Apparently the HC has not updated the HCCA since the
|
|
// last time the DPC ran. This is probably not good.
|
|
//
|
|
DeviceData->HcHCCA->HccaPad1 = 0xBAD2;
|
|
|
|
LOGENTRY(DeviceData, G, '_BD2', DeviceData,
|
|
lastDeadmanFrame,
|
|
currentDeadmanFrame);
|
|
|
|
LOGENTRY(DeviceData, G, '_bd2', DeviceData,
|
|
DeviceData->HcHCCA->HccaFrameNumber,
|
|
frameDelta);
|
|
|
|
break;
|
|
|
|
case 0xBAD2:
|
|
//
|
|
// Apparently the HC has not updated the HCCA since the
|
|
// last two times the DPC ran. This looks even worse.
|
|
// Assume the HC has become wedged.
|
|
//
|
|
DeviceData->HcHCCA->HccaPad1 = 0xBAD3;
|
|
|
|
LOGENTRY(DeviceData, G, '_BD3', DeviceData,
|
|
lastDeadmanFrame,
|
|
currentDeadmanFrame);
|
|
|
|
LOGENTRY(DeviceData, G, '_bd3', DeviceData,
|
|
DeviceData->HcHCCA->HccaFrameNumber,
|
|
frameDelta);
|
|
|
|
OHCI_KdPrint((DeviceData, 0,
|
|
"*** Warning: OHCI HC %08X appears to be wedged!\n",
|
|
DeviceData));
|
|
|
|
// Tell USBPORT to please reset the controller.
|
|
//
|
|
USBPORT_INVALIDATE_CONTROLLER(DeviceData,
|
|
UsbMpControllerNeedsHwReset);
|
|
|
|
break;
|
|
|
|
case 0xBAD3:
|
|
break;
|
|
|
|
default:
|
|
// Should not hit this case.
|
|
TEST_TRAP();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_ResetController(
|
|
PDEVICE_DATA DeviceData
|
|
)
|
|
/*++
|
|
Attempt to resurrect the HC after we have determined that it is dead.
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER HC;
|
|
ULONG HccaFrameNumber;
|
|
ULONG HcControl;
|
|
ULONG HcHCCA;
|
|
ULONG HcControlHeadED;
|
|
ULONG HcBulkHeadED;
|
|
ULONG HcFmInterval;
|
|
ULONG HcPeriodicStart;
|
|
ULONG HcLSThreshold;
|
|
HC_RH_DESCRIPTOR_A descrA;
|
|
ULONG port;
|
|
|
|
LOGENTRY(DeviceData, G, '_RHC', 0, 0, 0);
|
|
|
|
//
|
|
// Get the pointer to the HC Operational Registers
|
|
//
|
|
|
|
HC = DeviceData->HC;
|
|
|
|
//
|
|
// Save the last FrameNumber from the HCCA from when the HC froze
|
|
//
|
|
|
|
HccaFrameNumber = DeviceData->HcHCCA->HccaFrameNumber;
|
|
|
|
//
|
|
// Save current HC operational register values
|
|
//
|
|
|
|
// offset 0x04, save HcControl
|
|
//
|
|
HcControl = READ_REGISTER_ULONG(&HC->HcControl.ul);
|
|
|
|
// offset 0x18, save HcHCCA
|
|
//
|
|
HcHCCA = READ_REGISTER_ULONG(&HC->HcHCCA);
|
|
|
|
// offset 0x20, save HcControlHeadED
|
|
//
|
|
HcControlHeadED = READ_REGISTER_ULONG(&HC->HcControlHeadED);
|
|
|
|
// offset 0x28, save HcBulkHeadED
|
|
//
|
|
HcBulkHeadED = READ_REGISTER_ULONG(&HC->HcBulkHeadED);
|
|
|
|
// offset 0x34, save HcFmInterval
|
|
//
|
|
HcFmInterval = READ_REGISTER_ULONG(&HC->HcFmInterval.ul);
|
|
|
|
// offset 0x40, save HcPeriodicStart
|
|
//
|
|
HcPeriodicStart = READ_REGISTER_ULONG(&HC->HcPeriodicStart);
|
|
|
|
// offset 0x44, save HcLSThreshold
|
|
//
|
|
HcLSThreshold = READ_REGISTER_ULONG(&HC->HcLSThreshold);
|
|
|
|
|
|
//
|
|
// Reset the host controller
|
|
//
|
|
WRITE_REGISTER_ULONG(&HC->HcCommandStatus.ul, HcCmd_HostControllerReset);
|
|
KeStallExecutionProcessor(10);
|
|
|
|
|
|
//
|
|
// Restore / reinitialize HC operational register values
|
|
//
|
|
|
|
// offset 0x08, HcCommandStatus is set to zero on reset
|
|
|
|
// offset 0x0C, HcInterruptStatus is set to zero on reset
|
|
|
|
// offset 0x10, HcInterruptEnable is set to zero on reset
|
|
|
|
// offset 0x14, HcInterruptDisable is set to zero on reset
|
|
|
|
// offset 0x18, restore HcHCCA
|
|
//
|
|
WRITE_REGISTER_ULONG(&HC->HcHCCA, HcHCCA);
|
|
|
|
// offset 0x1C, HcPeriodCurrentED is set to zero on reset
|
|
|
|
// offset 0x20, restore HcControlHeadED
|
|
//
|
|
WRITE_REGISTER_ULONG(&HC->HcControlHeadED, HcControlHeadED);
|
|
|
|
// offset 0x24, HcControlCurrentED is set to zero on reset
|
|
|
|
// offset 0x28, restore HcBulkHeadED
|
|
//
|
|
WRITE_REGISTER_ULONG(&HC->HcBulkHeadED, HcBulkHeadED);
|
|
|
|
// offset 0x2C, HcBulkCurrentED is set to zero on reset
|
|
|
|
// offset 0x30, HcDoneHead is set to zero on reset
|
|
|
|
|
|
// It appears that writes to HcFmInterval don't stick unless the HC
|
|
// is in the operational state. Set the HC into the operational
|
|
// state at this point, but don't enable any list processing yet
|
|
// by setting any of the BLE, CLE, IE, or PLE bits.
|
|
//
|
|
WRITE_REGISTER_ULONG(&HC->HcControl.ul, HcCtrl_HCFS_USBOperational);
|
|
|
|
|
|
// offset 0x34, restore HcFmInterval
|
|
//
|
|
WRITE_REGISTER_ULONG(&HC->HcFmInterval.ul,
|
|
HcFmInterval | HcFmI_FRAME_INTERVAL_TOGGLE);
|
|
|
|
// offset 0x38, HcFmRemaining is set to zero on reset
|
|
|
|
// offset 0x3C, restore HcFmNumber
|
|
//
|
|
WRITE_REGISTER_ULONG(&HC->HcFmNumber, HccaFrameNumber);
|
|
|
|
// offset 0x40, restore HcPeriodicStart
|
|
//
|
|
WRITE_REGISTER_ULONG(&HC->HcPeriodicStart, HcPeriodicStart);
|
|
|
|
// offset 0x44, restore HcLSThreshold
|
|
//
|
|
WRITE_REGISTER_ULONG(&HC->HcLSThreshold, HcLSThreshold);
|
|
|
|
// Power on downstream ports
|
|
//
|
|
WRITE_REGISTER_ULONG(&HC->HcRhStatus,
|
|
HcRhS_SetGlobalPower | HcRhS_SetRemoteWakeupEnable);
|
|
|
|
descrA.ul = OHCI_ReadRhDescriptorA(DeviceData);
|
|
OHCI_ASSERT(DeviceData, (descrA.ul) && (!(descrA.ul & HcDescA_RESERVED)));
|
|
|
|
for (port = 0; port < descrA.s.NumberDownstreamPorts; port++)
|
|
{
|
|
WRITE_REGISTER_ULONG(&HC->HcRhPortStatus[port], HcRhPS_SetPortPower);
|
|
}
|
|
|
|
// offset 0x04, restore HcControl
|
|
//
|
|
HcControl &= ~(HcCtrl_HCFS_MASK);
|
|
HcControl |= HcCtrl_HCFS_USBOperational;
|
|
|
|
WRITE_REGISTER_ULONG(&HC->HcControl.ul, HcControl);
|
|
|
|
// offset 0x10, restore HcInterruptEnable (just turn everything on!)
|
|
//
|
|
WRITE_REGISTER_ULONG(&HC->HcInterruptEnable,
|
|
HcInt_MasterInterruptEnable | // 0x80000000
|
|
HcInt_OwnershipChange | // 0x40000000
|
|
HcInt_RootHubStatusChange | // 0x00000040
|
|
HcInt_FrameNumberOverflow | // 0x00000020
|
|
HcInt_UnrecoverableError | // 0x00000010
|
|
HcInt_ResumeDetected | // 0x00000008
|
|
HcInt_StartOfFrame | // 0x00000004
|
|
HcInt_WritebackDoneHead | // 0x00000002
|
|
HcInt_SchedulingOverrun // 0x00000001
|
|
);
|
|
}
|