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.
488 lines
12 KiB
488 lines
12 KiB
/*++
|
|
|
|
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; i<SOF_TD_COUNT; i++) {
|
|
td = &DeviceData->SofTdList->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; i<SOF_TD_COUNT; i++) {
|
|
td = &DeviceData->SofTdList->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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|