/*++ Copyright (c) 1999, 2000 Microsoft Corporation Module Name: int.c Abstract: interrupt service routine 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: 7-26-00 : created, jsenior --*/ #include "pch.h" //implements the following miniport functions: //non paged //UhciInterruptService //UhciInterruptDpc //UhciDisableInterrupts //UhciEnableInterrupts //UhciRHDisableIrq //UhciRHEnableIrq //UhciInterruptNextSOF BOOLEAN UhciInterruptService ( IN PDEVICE_DATA DeviceData ) /*++ Routine Description: Arguments: Return Value: --*/ { BOOLEAN usbInt; PHC_REGISTER reg; // USBINTR enabledIrqs; USBSTS irqStatus; reg = DeviceData->Registers; // assume it is not ours usbInt = FALSE; // see if we have lost the controller due to // a surprise remove if (UhciHardwarePresent(DeviceData) == FALSE) { return FALSE; } // get a mask of possible interrupts // enabledIrqs.us = READ_PORT_USHORT(®->UsbInterruptEnable.us); irqStatus.us = READ_PORT_USHORT(®->UsbStatus.us); // just look at the IRQ status bits irqStatus.us &= HcInterruptStatusMask; // irqStatus now possibly contains bits set for any currently // enabled interrupts if (irqStatus.HostSystemError || irqStatus.HostControllerProcessError) { UhciKdPrint((DeviceData, 0, "IrqStatus Error: %x\n", irqStatus.us)); } else if (irqStatus.us) { DeviceData->HCErrorCount = 0; } #if DBG // this usually means we have a bad TD in the schedule // we will need to debug this since the controller and/or // device will not function after this point if (irqStatus.HostControllerProcessError) { USHORT fn; fn = READ_PORT_USHORT(®->FrameNumber.us)&0x7ff; UhciKdPrint((DeviceData, 0, "HostControllerProcessError: %x\n", irqStatus.us)); UhciKdPrint((DeviceData, 0, "frame[]: %x\n", fn&0x7ff)); { //UhciDumpRegs(DeviceData); USHORT tmp; tmp = READ_PORT_USHORT(®->UsbCommand.us); UhciKdPrint((DeviceData, 0, "UsbCommand %x\n", tmp)); tmp = READ_PORT_USHORT(®->UsbStatus.us); UhciKdPrint((DeviceData, 0, "UsbStatus %x\n", tmp)); tmp = READ_PORT_USHORT(®->UsbInterruptEnable.us); UhciKdPrint((DeviceData, 0, "UsbInterruptEnable %x\n", tmp)); tmp = READ_PORT_USHORT(®->UsbCommand.us); UhciKdPrint((DeviceData, 0, "UsbCommand %x\n", tmp)); } TEST_TRAP(); } #endif // the halted bit alone does not indicate the interrupt // came from the controller if (irqStatus.UsbInterrupt || irqStatus.ResumeDetect || irqStatus.UsbError || irqStatus.HostSystemError || irqStatus.HostControllerProcessError) { DeviceData->IrqStatus = irqStatus.us; // Clear the condition WRITE_PORT_USHORT(®->UsbStatus.us, irqStatus.us); #if DBG #ifndef _WIN64 if (irqStatus.HostSystemError) { // something has gone terribly wrong UhciKdPrint((DeviceData, 0, "HostSystemError: %x\n", irqStatus.us)); TEST_TRAP(); } #endif #endif // indications are that this came from the // USB controller usbInt = TRUE; // disable all interrupts until the DPC for ISR runs WRITE_PORT_USHORT(®->UsbInterruptEnable.us, 0); } // // If bulk bandwidth reclamation is on and there's // nothing queued, then turn it off. // if (irqStatus.UsbInterrupt) { UhciUpdateCounter(DeviceData); if (!DeviceData->LastBulkQueueHead->HwQH.HLink.Terminate) { PHCD_QUEUEHEAD_DESCRIPTOR qh; BOOLEAN activeBulkTDs = FALSE; // This loop skips the td that has been inserted for // the PIIX4 problem, since it starts with the qh // the bulk queuehead is pointing at. // If the bulk queuehead is not pointing at anything, // then we're fine too, since it will have been // turned off already. for (qh = DeviceData->BulkQueueHead->NextQh; qh; qh = qh->NextQh) { if (!qh->HwQH.VLink.Terminate) { activeBulkTDs = TRUE; break; } } // // qh is pointing at either the first queuehead // with transfers pending or the bulk queuehead. // if (!activeBulkTDs) { UHCI_ASSERT(DeviceData, !qh) DeviceData->LastBulkQueueHead->HwQH.HLink.Terminate = 1; } } } if (irqStatus.HostControllerProcessError) { // // Force the schedule clean. // UhciCleanOutIsoch(DeviceData, TRUE); } else if (irqStatus.UsbInterrupt && DeviceData->IsoPendingTransfers) { // // Something completed. // UhciCleanOutIsoch(DeviceData, FALSE); #if 0 } else if (!DeviceData->IsoPendingTransfers) { // // Remove the rollover interrupt. // *( ((PULONG) (DeviceData->FrameListVA)) ) = DeviceData->RollOverTd->HwTD.LinkPointer.HwAddress; #endif } if (irqStatus.HostControllerProcessError) { if (DeviceData->HCErrorCount++ < UHCI_HC_MAX_ERRORS) { USBCMD command; // Attempt to recover. // It could just be that we overran. If so, // the above code that clears the schedule // should take care of it. command.us = READ_PORT_USHORT(®->UsbCommand.us); command.RunStop = 1; WRITE_PORT_USHORT(®->UsbCommand.us, command.us); UhciKdPrint((DeviceData, 0, "Attempted to recover from error\n")); } } return usbInt; } VOID UhciInterruptDpc ( IN PDEVICE_DATA DeviceData, IN BOOLEAN EnableInterrupts ) /*++ Routine Description: process an interrupt Arguments: Return Value: --*/ { PHC_REGISTER reg; USBSTS irqStatus, tmp; PLIST_ENTRY listEntry; PENDPOINT_DATA endpointData; reg = DeviceData->Registers; // ack all status bits asserted now //tmp.us = READ_PORT_USHORT(®->UsbStatus.us); tmp.us = DeviceData->IrqStatus; DeviceData->IrqStatus = 0; LOGENTRY(DeviceData, G, '_idp', tmp.us, 0, 0); //WRITE_PORT_USHORT(®->UsbStatus.us, tmp.us); // now process status bits aserted, // just look at the IRQ status bits irqStatus.us = tmp.us & HcInterruptStatusMask; if (irqStatus.UsbInterrupt || irqStatus.UsbError) { LOGENTRY(DeviceData, G, '_iEP', irqStatus.us, 0, 0); USBPORT_INVALIDATE_ENDPOINT(DeviceData, NULL); } if (EnableInterrupts) { LOGENTRY(DeviceData, G, '_iEE', 0, 0, 0); WRITE_PORT_USHORT(®->UsbInterruptEnable.us, DeviceData->EnabledInterrupts.us); } } VOID USBMPFN UhciDisableInterrupts( IN PDEVICE_DATA DeviceData ) /*++ Routine Description: Arguments: Return Value: --*/ { USHORT legsup; PHC_REGISTER reg; UhciKdPrint((DeviceData, 2, "Disable interrupts\n")); LOGENTRY(DeviceData, G, '_DIn', 0, 0, 0); reg = DeviceData->Registers; WRITE_PORT_USHORT(®->UsbInterruptEnable.us, 0); if (DeviceData->ControllerFlavor != UHCI_Ich2_1 && DeviceData->ControllerFlavor != UHCI_Ich2_2) { // // change the state of the PIRQD routing bit // USBPORT_READ_CONFIG_SPACE( DeviceData, &legsup, LEGACY_BIOS_REGISTER, sizeof(legsup)); LOGENTRY(DeviceData, G, '_leg', 0, legsup, 0); // clear the PIRQD routing bit legsup &= ~LEGSUP_USBPIRQD_EN; USBPORT_WRITE_CONFIG_SPACE( DeviceData, &legsup, LEGACY_BIOS_REGISTER, sizeof(legsup)); } } VOID UhciFlushInterrupts( IN PDEVICE_DATA DeviceData ) /*++ Routine Description: used to flush rougue interrupts from the controller after power events Arguments: Return Value: --*/ { PHC_REGISTER reg; LOGENTRY(DeviceData, G, '_FIn', 0, 0, 0); UhciKdPrint((DeviceData, 2, "Enable interrupts\n")); reg = DeviceData->Registers; // before writing the PIRQD register ack any eronious interrupts // the controller may be asserting -- it should not be asserting // at all but often is WRITE_PORT_USHORT(®->UsbStatus.us, 0xFFFF); } VOID USBMPFN UhciEnableInterrupts( IN PDEVICE_DATA DeviceData ) /*++ Routine Description: Arguments: Return Value: --*/ { USHORT legsup; PHC_REGISTER reg; LOGENTRY(DeviceData, G, '_EIn', 0, 0, 0); UhciKdPrint((DeviceData, 2, "Enable interrupts\n")); reg = DeviceData->Registers; // // change the state of the PIrQD routing bit // USBPORT_READ_CONFIG_SPACE( DeviceData, &legsup, LEGACY_BIOS_REGISTER, sizeof(legsup)); LOGENTRY(DeviceData, G, '_leg', 0, legsup, 0); // clear the PIRQD routing bit legsup |= LEGSUP_USBPIRQD_EN; USBPORT_WRITE_CONFIG_SPACE( DeviceData, &legsup, LEGACY_BIOS_REGISTER, sizeof(legsup)); WRITE_PORT_USHORT(®->UsbInterruptEnable.us, DeviceData->EnabledInterrupts.us); } VOID UhciRHDisableIrq( IN PDEVICE_DATA DeviceData ) { // Uhci doesn't have this IRQ } VOID UhciRHEnableIrq( IN PDEVICE_DATA DeviceData ) { // Uhci doesn't have this IRQ } #define UHCI_SOF_LATENCY 2 VOID UhciInterruptNextSOF( IN PDEVICE_DATA DeviceData ) { ULONG i, frame, offset, cf; PHCD_TRANSFER_DESCRIPTOR td; BOOLEAN found = FALSE; cf = UhciGet32BitFrameNumber(DeviceData); // find a TD for (i=0; iSofTdList->Td[i]; UHCI_ASSERT(DeviceData, td->Sig == SIG_HCD_SOFTD); // use transferconext to hold req frame frame = td->RequestFrame; if (frame == cf+UHCI_SOF_LATENCY) { // There's already one queued found = TRUE; break; } if (frame < cf) { td->RequestFrame = (cf+UHCI_SOF_LATENCY); LOGENTRY(DeviceData, G, '_SOF', td, td->RequestFrame, cf); // insert TD td->HwTD.LinkPointer.HwAddress = 0; INSERT_ISOCH_TD(DeviceData, td, td->RequestFrame); found = TRUE; break; } } if (!found) { TEST_TRAP(); } // recycle any old SOF interrupt TDs for (i=0; iSofTdList->Td[i]; UHCI_ASSERT(DeviceData, td->Sig == SIG_HCD_SOFTD); // use transferconext to hold req frame frame = td->RequestFrame; if (frame && (frame < cf || frame - cf > UHCI_MAX_FRAME)) { td->RequestFrame = 0; } } }