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