Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1019 lines
27 KiB

/*++
Copyright (c) 1999, 2000 Microsoft Corporation
Module Name:
roothub.c
Abstract:
miniport root hub
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:
1-1-00 : created, jdunn
implements the following miniport functions:
MINIPORT_RH_GetStatus
MINIPORT_RH_GetPortStatus
MINIPORT_RH_GethubStatus
MINIPORT_RH_SetFeaturePortReset
MINIPORT_RH_SetFeaturePortSuspend
MINIPORT_RH_SetFeaturePortPower
MINIPORT_RH_ClearFeaturePortEnable
MINIPORT_RH_ClearFeaturePortSuspend
MINIPORT_RH_ClearFeaturePortPower
MINIPORT_RH_ClearFeaturePortConnectChange
MINIPORT_RH_ClearFeaturePortResetChange
MINIPORT_RH_ClearFeaturePortEnableChange
MINIPORT_RH_ClearFeaturePortSuspendChange
MINIPORT_RH_ClearFeaturePortOvercurrentChange
--*/
#include "common.h"
#include "usbpriv.h"
typedef struct _EHCI_PORT_EVENT_CONTEXT {
USHORT PortNumber;
} EHCI_PORT_EVENT_CONTEXT, *PEHCI_PORT_EVENT_CONTEXT;
VOID
EHCI_RH_GetRootHubData(
PDEVICE_DATA DeviceData,
OUT PROOTHUB_DATA HubData
)
/*++
return info about the root hub
--*/
{
HubData->NumberOfPorts =
DeviceData->NumberOfPorts;
if (DeviceData->PortPowerControl == 1) {
HubData->HubCharacteristics.PowerSwitchType =
USBPORT_RH_POWER_SWITCH_PORT;
} else {
HubData->HubCharacteristics.PowerSwitchType =
USBPORT_RH_POWER_SWITCH_GANG;
}
HubData->HubCharacteristics.Reserved = 0;
HubData->HubCharacteristics.OverCurrentProtection = 0;
HubData->HubCharacteristics.CompoundDevice = 0;
HubData->PowerOnToPowerGood = 2;
// this value is the current consumed by the hub
// brains, for the embeded hub this doesn't make
// much sense.
//
// so we report zero
HubData->HubControlCurrent = 0;
LOGENTRY(DeviceData, G, '_hub', HubData->NumberOfPorts,
DeviceData->PortPowerControl, 0);
}
USB_MINIPORT_STATUS
EHCI_RH_GetStatus(
PDEVICE_DATA DeviceData,
OUT PUSHORT Status
)
/*++
get the device status
--*/
{
// the root hub is self powered
*Status = USB_GETSTATUS_SELF_POWERED;
return USBMP_STATUS_SUCCESS;
}
USB_MINIPORT_STATUS
EHCI_RH_ClearFeaturePortEnable (
PDEVICE_DATA DeviceData,
USHORT PortNumber
)
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
hcOp = DeviceData->OperationalRegisters;
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
EHCI_KdPrint((DeviceData, 0, "port[%d] disable (1) %x\n", PortNumber, port.ul));
port.PortEnable = 0;
MASK_CHANGE_BITS(port);
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul,
port.ul);
return USBMP_STATUS_SUCCESS;
}
USB_MINIPORT_STATUS
EHCI_RH_ClearFeaturePortPower (
PDEVICE_DATA DeviceData,
USHORT PortNumber
)
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
// turn power off
hcOp = DeviceData->OperationalRegisters;
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
port.PortPower = 0;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul,
port.ul);
return USBMP_STATUS_SUCCESS;
}
VOID
EHCI_RH_PortResumeComplete(
PDEVICE_DATA DeviceData,
PVOID Context
)
/*++
complete a port resume
--*/
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
PEHCI_PORT_EVENT_CONTEXT portResumeContext = Context;
USHORT portNumber;
hcOp = DeviceData->OperationalRegisters;
portNumber = portResumeContext->PortNumber;
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[portNumber-1].ul);
LOGENTRY(DeviceData, G, '_pRS', port.ul,
DeviceData->PortSuspendChange, portNumber);
EHCI_KdPrint((DeviceData, 1, "port[%d] resume (1) %x\n", portNumber, port.ul));
// writing a 0 stops resume
MASK_CHANGE_BITS(port);
port.ForcePortResume = 0;
port.PortSuspend = 0;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[portNumber-1].ul,
port.ul);
// indicate a change to suspend state ie resume complete
SET_BIT(DeviceData->PortSuspendChange, portNumber-1);
}
USB_MINIPORT_STATUS
EHCI_RH_ClearFeaturePortSuspend (
PDEVICE_DATA DeviceData,
USHORT PortNumber
)
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
EHCI_PORT_EVENT_CONTEXT portResumeContext;
// resume the port
hcOp = DeviceData->OperationalRegisters;
// mask off CC chirping on this port
SET_BIT(DeviceData->PortPMChirp, PortNumber-1);
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
// writing a 1 generates resume signalling
port.ForcePortResume = 1;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul,
port.ul);
// time it
portResumeContext.PortNumber = PortNumber;
// some hubs require us to wait longer if the downstream
// device drivers resume for > 10 ms. Looks like we need
// 50 for the NEC B1 hub.
USBPORT_REQUEST_ASYNC_CALLBACK(DeviceData,
50, // callback in ms,
&portResumeContext,
sizeof(portResumeContext),
EHCI_RH_PortResumeComplete);
return USBMP_STATUS_SUCCESS;
}
USB_MINIPORT_STATUS
EHCI_RH_ClearFeaturePortSuspendChange (
PDEVICE_DATA DeviceData,
USHORT PortNumber
)
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
hcOp = DeviceData->OperationalRegisters;
CLEAR_BIT(DeviceData->PortSuspendChange, PortNumber-1);
return USBMP_STATUS_SUCCESS;
}
USB_MINIPORT_STATUS
EHCI_RH_ClearFeaturePortOvercurrentChange (
PDEVICE_DATA DeviceData,
USHORT PortNumber
)
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
EHCI_KdPrint((DeviceData, 1,
"'EHCI_RH_ClearFeatureOvercurrentChange\n"));
hcOp = DeviceData->OperationalRegisters;
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
MASK_CHANGE_BITS(port);
port.OvercurrentChange = 1;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul,
port.ul);
return USBMP_STATUS_SUCCESS;
}
USB_MINIPORT_STATUS
EHCI_RH_GetPortStatus(
PDEVICE_DATA DeviceData,
USHORT PortNumber,
PRH_PORT_STATUS portStatus
)
/*++
get the status of a partuclar port
--*/
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
hcOp = DeviceData->OperationalRegisters;
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
portStatus->ul = 0;
LOGENTRY(DeviceData, G, '_Pp1', PortNumber, port.ul, 0);
// low speed detect, if low speed then do an immediate
// handoff to the CC
// This field is only valid if enable status is 0 and
// connect status is 1
if ((port.LineStatus == 1) &&
port.PortOwnedByCC == 0 &&
port.PortSuspend == 0 &&
port.PortEnable == 0 &&
port.PortConnect == 1 ) {
EHCI_KdPrint((DeviceData, 1, "'low speed device detected\n"));
// low speed device detected
port.PortOwnedByCC = 1;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul,
port.ul);
return USBMP_STATUS_SUCCESS;
}
// map the bits to the port status structure
portStatus->Connected =
port.PortConnect;
portStatus->Enabled =
port.PortEnable;
portStatus->Suspended =
port.PortSuspend;
portStatus->OverCurrent =
port.OvercurrentActive;
portStatus->Reset =
port.PortReset;
portStatus->PowerOn =
port.PortPower;
portStatus->OwnedByCC =
port.PortOwnedByCC;
if (portStatus->Connected == 1) {
portStatus->HighSpeed = 1;
portStatus->LowSpeed = 0;
} else {
// report high speed when no device connected
// this should work around a bug in the usbhub
// driver -- the hub driver does not refresh the
// port status register if the first reset attempt
// fails.
portStatus->HighSpeed = 1;
}
// chirping support allows us to use the
// port change status bit
if (port.PortConnectChange == 1) {
SET_BIT(DeviceData->PortConnectChange, PortNumber-1);
}
portStatus->EnableChange =
port.PortEnableChange;
portStatus->OverCurrentChange =
port.OvercurrentChange;
// these change bits must be emulated
if (TEST_BIT(DeviceData->PortResetChange, PortNumber-1)) {
portStatus->ResetChange = 1;
}
if (TEST_BIT(DeviceData->PortConnectChange, PortNumber-1)) {
portStatus->ConnectChange = 1;
}
if (TEST_BIT(DeviceData->PortSuspendChange, PortNumber-1)) {
portStatus->SuspendChange = 1;
}
LOGENTRY(DeviceData, G, '_gps',
PortNumber, portStatus->ul, port.ul);
return USBMP_STATUS_SUCCESS;
}
VOID
EHCI_RH_FinishReset(
PDEVICE_DATA DeviceData,
PVOID Context
)
/*++
complete a port reset
--*/
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
PEHCI_PORT_EVENT_CONTEXT portResetContext = Context;
USHORT portNumber;
ULONG NecUsb2HubHack = 0;
hcOp = DeviceData->OperationalRegisters;
portNumber = portResetContext->PortNumber;
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[portNumber-1].ul);
EHCI_KdPrint((DeviceData, 0, "port[%d] reset (4) %x\n", portNumber, port.ul));
if (port.ul == 0xFFFFFFFF) {
// just bail if hardware disappears
return;
}
// at this point we will know if this is a high speed
// device -- if it is not then we need to hand the port
// to the CC
// port enable of zero means we have a full or low speed
// device (ie not chirping).
#if DBG
if (port.PortConnect == 0) {
EHCI_KdPrint((DeviceData, 0, "HS device dropped\n"));
}
#endif
if (port.PortEnable == 0 &&
port.PortConnect == 1 &&
port.PortConnectChange == 0) {
// do the handoff
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[portNumber-1].ul);
port.PortOwnedByCC = 1;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[portNumber-1].ul,
port.ul);
// do not indicate a reset change, this will cause the
// hub driver to timeout the reset and detect that
// no device is connected. when this occurs on a USB 2
// controller the hub driver will ignore the error.
//CLEAR_BIT(DeviceData->PortResetChange, portNumber-1);
SET_BIT(DeviceData->PortResetChange, portNumber-1);
} else {
// we have a USB 2.0 device, indicate the reset change
// NOTE if the device dropped off the bus (NEC USB 2 hub or
// user removed it) we still indicate a reset change on high
// speed
SET_BIT(DeviceData->PortResetChange, portNumber-1);
USBPORT_INVALIDATE_ROOTHUB(DeviceData);
}
CLEAR_BIT(DeviceData->PortPMChirp, portNumber-1);
}
VOID
EHCI_RH_PortResetComplete(
PDEVICE_DATA DeviceData,
PVOID Context
)
/*++
complete a port reset
--*/
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
PEHCI_PORT_EVENT_CONTEXT portResetContext = Context;
USHORT portNumber;
BOOLEAN forceHighSpeed = FALSE;
ULONG microsecs;
hcOp = DeviceData->OperationalRegisters;
portNumber = portResetContext->PortNumber;
EHCI_RH_PortResetComplete_Retry:
microsecs = 0;
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[portNumber-1].ul);
LOGENTRY(DeviceData, G, '_prC', port.ul,
DeviceData->PortResetChange, portNumber);
EHCI_KdPrint((DeviceData, 0, "port[%d] reset (1) %x\n", portNumber, port.ul));
// writing a 0 stops reset
MASK_CHANGE_BITS(port);
port.PortReset = 0;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[portNumber-1].ul,
port.ul);
// wait for reset to go low -- this should be on the order of
// microseconds
do {
KeStallExecutionProcessor(20); // spec says 10 microseconds
// Intel controller needs 20
microsecs+=20;
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[portNumber-1].ul);
EHCI_KdPrint((DeviceData, 1, "port[%d] reset (2) %x\n",
portNumber, port.ul));
if (microsecs > USBEHCI_MAX_RESET_TIME) {
// > 1 microframe (125 us) has passed, retry
EHCI_KdPrint((DeviceData, 0, "port[%d] reset (timeout) %x\n", portNumber, port.ul));
goto EHCI_RH_PortResetComplete_Retry;
}
// bail if HW is gone
} while (port.PortReset == 1 && port.ul != 0xFFFFFFFF);
EHCI_KdPrint((DeviceData, 0, "port[%d] reset (3) %x\n", portNumber, port.ul));
USBPORT_REQUEST_ASYNC_CALLBACK(DeviceData,
50, // callback in 10 ms,
portResetContext,
sizeof(*portResetContext),
EHCI_RH_FinishReset);
}
USB_MINIPORT_STATUS
EHCI_RH_SetFeaturePortReset(
PDEVICE_DATA DeviceData,
USHORT PortNumber
)
/*++
Put a port in reset
--*/
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
EHCI_PORT_EVENT_CONTEXT portResetContext;
hcOp = DeviceData->OperationalRegisters;
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
LOGENTRY(DeviceData, G, '_spr', port.ul,
0, PortNumber);
// mask off CC chirping on this port
SET_BIT(DeviceData->PortPMChirp, PortNumber-1);
// do a normal reset sequence
LOGENTRY(DeviceData, G, '_res', port.ul, 0, PortNumber);
MASK_CHANGE_BITS(port);
port.PortEnable = 0;
port.PortReset = 1;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul, port.ul);
// schedule a callback
portResetContext.PortNumber = PortNumber;
// note that usbport calls us back with a copy of this
// structure not the pointer to the original structure
USBPORT_REQUEST_ASYNC_CALLBACK(DeviceData,
50, // callback in x ms,
&portResetContext,
sizeof(portResetContext),
EHCI_RH_PortResetComplete);
return USBMP_STATUS_SUCCESS;
}
USB_MINIPORT_STATUS
EHCI_RH_SetFeaturePortSuspend(
PDEVICE_DATA DeviceData,
USHORT PortNumber
)
/*++
Put a port in suspend
--*/
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
hcOp = DeviceData->OperationalRegisters;
// NOTE:
// there should be no transactions in progress at the
// time we suspend the port.
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
LOGENTRY(DeviceData, G, '_sps', port.ul,
0, PortNumber);
// writing a 1 suspends the port
MASK_CHANGE_BITS(port);
port.PortSuspend = 1;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul,
port.ul);
// wiat 1 microframe for current transaction to finish
KeStallExecutionProcessor(125);
return USBMP_STATUS_SUCCESS;
}
USB_MINIPORT_STATUS
EHCI_RH_SetFeaturePortPower(
PDEVICE_DATA DeviceData,
USHORT PortNumber
)
/*++
--*/
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
hcOp = DeviceData->OperationalRegisters;
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
LOGENTRY(DeviceData, G, '_spp', port.ul,
0, PortNumber);
// writing a 1 turns on power
MASK_CHANGE_BITS(port);
port.PortPower = 1;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul,
port.ul);
return USBMP_STATUS_SUCCESS;
}
USB_MINIPORT_STATUS
EHCI_RH_SetFeaturePortEnable(
PDEVICE_DATA DeviceData,
USHORT PortNumber
)
/*++
--*/
{
// do nothing, independent enable not supported
return USBMP_STATUS_SUCCESS;
}
USB_MINIPORT_STATUS
EHCI_RH_ClearFeaturePortConnectChange(
PDEVICE_DATA DeviceData,
USHORT PortNumber
)
/*++
--*/
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
hcOp = DeviceData->OperationalRegisters;
//
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
LOGENTRY(DeviceData, G, '_pcc', port.ul,
0, PortNumber);
// writing a 1 zeros the change bit
if (port.PortConnectChange == 1) {
// mask off other change bits
MASK_CHANGE_BITS(port);
port.PortConnectChange = 1;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul,
port.ul);
}
CLEAR_BIT(DeviceData->PortConnectChange, PortNumber-1);
return USBMP_STATUS_SUCCESS;
}
USB_MINIPORT_STATUS
EHCI_RH_ClearFeaturePortEnableChange(
PDEVICE_DATA DeviceData,
USHORT PortNumber
)
/*++
--*/
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
LOGENTRY(DeviceData, G, '_cpe', PortNumber, 0, 0);
hcOp = DeviceData->OperationalRegisters;
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
MASK_CHANGE_BITS(port);
port.PortEnableChange = 1;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul, port.ul);
return USBMP_STATUS_SUCCESS;
}
USB_MINIPORT_STATUS
EHCI_RH_GetHubStatus(
PDEVICE_DATA DeviceData,
OUT PRH_HUB_STATUS HubStatus
)
/*++
--*/
{
// nothing intersting for the root
// hub to report
HubStatus->ul = 0;
return USBMP_STATUS_SUCCESS;
}
USB_MINIPORT_STATUS
EHCI_RH_ClearFeaturePortResetChange(
PDEVICE_DATA DeviceData,
USHORT PortNumber
)
/*++
--*/
{
CLEAR_BIT(DeviceData->PortResetChange, PortNumber-1);
return USBMP_STATUS_SUCCESS;
}
VOID
EHCI_OptumtuseratePort(
PDEVICE_DATA DeviceData,
USHORT PortNumber
)
/*++
Forces the port to high speed mode.
NOTE:
Current mechanism only works on the NEC controller.
--*/
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
LOGENTRY(DeviceData, G, '_obt', PortNumber, 0, 0);
hcOp = DeviceData->OperationalRegisters;
port.ul = 0x5100a;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul,
port.ul);
KeStallExecutionProcessor(10); //stall for 10 microseconds
// force high speed mode on the port
port.ul = 0x01005;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul,
port.ul);
KeStallExecutionProcessor(100); //stall for 10 microseconds
}
USB_MINIPORT_STATUS
EHCI_RH_UsbprivRootPortStatus(
PDEVICE_DATA DeviceData,
ULONG ParameterLength,
PVOID Parameters
)
{
PUSBPRIV_ROOTPORT_STATUS portStatusParams;
PHC_OPERATIONAL_REGISTER hcOp;
PRH_PORT_STATUS portStatus;
PORTSC port;
USHORT portNumber;
if (ParameterLength < sizeof(USBPRIV_ROOTPORT_STATUS))
{
return (USBMP_STATUS_FAILURE);
}
//
// Read the port status for this port from the registers
//
hcOp = DeviceData->OperationalRegisters;
portStatusParams = (PUSBPRIV_ROOTPORT_STATUS) Parameters;
portNumber = portStatusParams->PortNumber;
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[portNumber-1].ul);
LOGENTRY(DeviceData, G, '_Up1', portNumber, port.ul, 0);
//
// Check to see if the port is resuming. If so, clear the bit and
// reenable the port.
//
if (port.ForcePortResume)
{
//
// Clear the port resume
//
USBPORT_WAIT(DeviceData, 50);
MASK_CHANGE_BITS(port);
port.ForcePortResume = 0;
port.PortSuspend = 0;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[portNumber-1].ul, port.ul);
//
// Reread the port status
//
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[portNumber-1].ul);
SET_BIT(DeviceData->PortSuspendChange, portNumber-1);
LOGENTRY(DeviceData, G, '_Up2', portNumber, port.ul, 0);
}
//
// Map the current port information to the port status
//
portStatus = &portStatusParams->PortStatus;
portStatus->ul = 0;
portStatus->Connected =
port.PortConnect;
portStatus->Enabled =
port.PortEnable;
portStatus->Suspended =
port.PortSuspend;
portStatus->OverCurrent =
port.OvercurrentActive;
portStatus->Reset =
port.PortReset;
portStatus->PowerOn =
port.PortPower;
portStatus->OwnedByCC =
port.PortOwnedByCC;
if (portStatus->Connected == 1) {
portStatus->HighSpeed = 1;
portStatus->LowSpeed = 0;
} else {
// report high speed when no device connected
// this should work around a bug in the usbhub
// driver -- the hub driver does not refresh the
// port status register if the first reset attempt
// fails.
portStatus->HighSpeed = 1;
}
portStatus->ConnectChange =
port.PortConnectChange;
portStatus->EnableChange =
port.PortEnableChange;
portStatus->OverCurrentChange =
port.OvercurrentChange;
// these change bits must be emulated
if (TEST_BIT(DeviceData->PortResetChange, portNumber-1)) {
portStatus->ResetChange = 1;
}
if (TEST_BIT(DeviceData->PortConnectChange, portNumber-1)) {
portStatus->ConnectChange = 1;
}
if (TEST_BIT(DeviceData->PortSuspendChange, portNumber-1)) {
portStatus->SuspendChange = 1;
}
LOGENTRY(DeviceData, G, '_Ups',
portNumber, portStatus->ul, port.ul);
return USBMP_STATUS_SUCCESS;
}
USB_MINIPORT_STATUS
EHCI_RH_ChirpRootPort(
PDEVICE_DATA DeviceData,
USHORT PortNumber
)
{
PHC_OPERATIONAL_REGISTER hcOp;
PORTSC port;
EHCI_PORT_EVENT_CONTEXT portResetContext;
ULONG mics;
hcOp = DeviceData->OperationalRegisters;
#if DBG
{
USBCMD cmd;
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul);
EHCI_ASSERT(DeviceData, cmd.HostControllerRun == 1);
}
#endif
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
LOGENTRY(DeviceData, G, '_chr', port.ul,
0, PortNumber);
EHCI_KdPrint((DeviceData, 0, ">port[%d] chirp %x\n", PortNumber, port.ul));
if (TEST_BIT(DeviceData->PortPMChirp, PortNumber-1)) {
// skip the chirp if we have already done this
EHCI_KdPrint((DeviceData, 0, "<skip port chirp[%d] %x\n", PortNumber, port.ul));
return USBMP_STATUS_SUCCESS;
}
if (!port.PortPower) {
// skip if not powered, this will cause us to
// bypass the chirp if the controller has not initialized
// such as in the case of BOOT
EHCI_KdPrint((DeviceData, 0, "<skip port chirp[%d] %x, no power\n", PortNumber, port.ul));
return USBMP_STATUS_SUCCESS;
}
// port is connect and not enabled and not owned by CC
// therefore we should probably chirp it
if (port.PortConnect &&
!port.PortEnable &&
!port.PortOwnedByCC) {
//TEST_TRAP();
// quick check for handoff of LS devices
if ((port.LineStatus == 1) &&
port.PortOwnedByCC == 0 &&
port.PortSuspend == 0 &&
port.PortEnable == 0 &&
port.PortConnect == 1 ) {
// low speed device detected
port.PortOwnedByCC = 1;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul,
port.ul);
EHCI_KdPrint((DeviceData, 0, ">port chirp[%d] %x, ->cc(1)\n", PortNumber,
port.ul));
return USBMP_STATUS_SUCCESS;
}
// do a normal reset sequence
LOGENTRY(DeviceData, G, '_rss', port.ul, 0, PortNumber);
// set reset and clear connect change
port.PortEnable = 0;
port.PortReset = 1;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul, port.ul);
USBPORT_WAIT(DeviceData, 10);
EHCI_RH_ChirpRootPort_Retry:
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
MASK_CHANGE_BITS(port);
port.PortReset = 0;
mics = 0;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul, port.ul);
// wait for reset to go low -- this should be on the order of
// microseconds
do {
// writing a 0 stops reset
KeStallExecutionProcessor(20); // spec says 10 microseconds
// Intel controller needs 20
mics +=20;
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
EHCI_KdPrint((DeviceData, 1, "port reset (2) %x\n", port.ul));
if (mics > USBEHCI_MAX_RESET_TIME) {
// reset did not clear in 1 microframe, try again to clear it
goto EHCI_RH_ChirpRootPort_Retry;
}
} while (port.PortReset == 1);
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
if (port.PortEnable == 0) {
// do the handoff
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
port.PortOwnedByCC = 1;
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul,
port.ul);
EHCI_KdPrint((DeviceData, 0, "<port chirp[%d] %x, ->cc(2)\n", PortNumber,
port.ul));
} else {
// clear the enable bit so the device does not listen
// on address 0
port.ul = READ_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul);
port.PortEnable = 0;
MASK_CHANGE_BITS(port);
// allow some SOFs before we disable
// The Cypress USB 2 device will drop off the bus if it doen't see
// any SOFs
USBPORT_WAIT(DeviceData, 10);
SET_BIT(DeviceData->PortPMChirp, PortNumber-1);
WRITE_REGISTER_ULONG(&hcOp->PortRegister[PortNumber-1].ul,
port.ul);
EHCI_KdPrint((DeviceData, 0, "<chirp port[%d] disable %x\n",
PortNumber, port.ul));
}
} else {
EHCI_KdPrint((DeviceData, 0, "<no port chirp[%d] %x\n", PortNumber, port.ul));
}
return USBMP_STATUS_SUCCESS;
}