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