/*++ 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, "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, "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, "