windows-nt/Source/XPSP1/NT/base/busdrv/pccard/pcmcibus/ctlr.c
2020-09-26 16:20:57 +08:00

1339 lines
36 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1997-2000 Microsoft Corporation
Module Name:
ctlr.c
Abstract:
This module contains code to support starting and stopping the
pcmcia controller.
Author:
Neil Sandlin (neilsa) 1-Jun-1999
Environment:
Kernel mode
Revision History :
--*/
#include "pch.h"
//
// Internal References
//
NTSTATUS
PcmciaInitializeController(
IN PDEVICE_OBJECT Fdo
);
NTSTATUS
PcmciaGetPciControllerType(
IN PDEVICE_OBJECT Pdo,
IN PDEVICE_OBJECT Fdo
);
NTSTATUS
PcmciaCreateFdo(
IN PDRIVER_OBJECT DriverObject,
OUT PDEVICE_OBJECT *NewDeviceObject
);
BOOLEAN
PcmciaInterrupt(
IN PKINTERRUPT InterruptObject,
IN PVOID Context
);
VOID
PcmciaInterruptDpc(
IN PKDPC Dpc,
IN PDEVICE_OBJECT DeviceObject,
IN PVOID SystemContext1,
IN PVOID SystemContext2
);
VOID
PcmciaTimerDpc(
IN PKDPC Dpc,
IN PDEVICE_OBJECT DeviceObject,
IN PVOID SystemContext1,
IN PVOID SystemContext2
);
VOID
PcmciaDpc(
IN PKDPC Dpc,
IN PDEVICE_OBJECT DeviceObject,
IN PVOID SystemContext1,
IN PVOID SystemContext2
);
PUNICODE_STRING DriverRegistryPath;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PcmciaAddDevice)
#pragma alloc_text(PAGE, PcmciaCreateFdo)
#pragma alloc_text(PAGE, PcmciaStartPcmciaController)
#pragma alloc_text(PAGE, PcmciaSetControllerType)
#pragma alloc_text(PAGE, PcmciaGetPciControllerType)
#endif
NTSTATUS
PcmciaAddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT Pdo
)
/*++
Routine Description:
This routine creates functional device objects for each Pcmcia controller in the
system and attaches them to the physical device objects for the controllers
Arguments:
DriverObject - a pointer to the object for this driver
PhysicalDeviceObject - a pointer to the physical object we need to attach to
Return Value:
Status from device creation and initialization
--*/
{
PDEVICE_OBJECT fdo = NULL;
PDEVICE_OBJECT lowerDevice = NULL;
PFDO_EXTENSION deviceExtension;
ULONG resultLength;
NTSTATUS status;
PAGED_CODE();
DebugPrint((PCMCIA_DEBUG_PNP, "AddDevice Entered with pdo %x\n", Pdo));
if (Pdo == NULL) {
//
// Have we been asked to do detection on our own?
// if so just return no more devices
//
DebugPrint((PCMCIA_DEBUG_FAIL, "PcmciaAddDevice - asked to do detection\n"));
return STATUS_NO_MORE_ENTRIES;
}
//
// create and initialize the new functional device object
//
status = PcmciaCreateFdo(DriverObject, &fdo);
if (!NT_SUCCESS(status)) {
DebugPrint((PCMCIA_DEBUG_FAIL, "PcmciaAddDevice - error creating Fdo [%#08lx]\n",
status));
return status;
}
deviceExtension = fdo->DeviceExtension;
KeInitializeTimer(&deviceExtension->EventTimer);
KeInitializeDpc(&deviceExtension->EventDpc, PcmciaDpc, fdo);
KeInitializeTimer(&deviceExtension->PowerTimer);
KeInitializeDpc(&deviceExtension->PowerDpc, PcmciaFdoPowerWorkerDpc, deviceExtension);
InitializeListHead(&deviceExtension->PdoPowerRetryList);
KeInitializeDpc(&deviceExtension->PdoPowerRetryDpc, PcmciaFdoRetryPdoPowerRequest, deviceExtension);
//
// Layer our FDO on top of the PDO
//
//
lowerDevice = IoAttachDeviceToDeviceStack(fdo,Pdo);
//
// No status. Do the best we can.
//
if (lowerDevice == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto cleanupExit;
};
deviceExtension->LowerDevice = lowerDevice;
deviceExtension->Pdo = Pdo;
status = IoGetDeviceProperty(Pdo,
DevicePropertyLegacyBusType,
sizeof(INTERFACE_TYPE),
(PVOID)&deviceExtension->InterfaceType,
&resultLength);
if (!NT_SUCCESS(status)) {
//
// Probably a legacy pcic device
//
deviceExtension->InterfaceType = InterfaceTypeUndefined;
}
//
// Get our controller type
//
deviceExtension->ControllerType = PcmciaInvalidControllerType;
if (deviceExtension->InterfaceType == PCIBus) {
status = PcmciaGetPciControllerType(Pdo, fdo);
if (!NT_SUCCESS(status)) {
goto cleanupExit;
}
} else {
PCMCIA_CONTROLLER_TYPE controllerType;
status = PcmciaGetLegacyDetectedControllerType(deviceExtension->Pdo,
&controllerType);
if (NT_SUCCESS(status)) {
PcmciaSetControllerType(deviceExtension, controllerType);
}
}
//
// Do some cardbus specific initialization
//
if (CardBusExtension(deviceExtension)) {
BOOLEAN OnDebugPath;
ACPI_INTERFACE_STANDARD AcpiInterface;
USHORT word;
//
// Get the pci/cardbus private interface
//
status = PcmciaGetInterface(Pdo,
&GUID_PCI_CARDBUS_INTERFACE_PRIVATE,
sizeof(PCI_CARDBUS_INTERFACE_PRIVATE),
(PINTERFACE) &deviceExtension->PciCardBusInterface
);
if (!NT_SUCCESS(status)) {
goto cleanupExit;
}
status = deviceExtension->PciCardBusInterface.GetLocation(Pdo,
&deviceExtension->PciBusNumber,
&deviceExtension->PciDeviceNumber,
&deviceExtension->PciFunctionNumber,
&OnDebugPath);
if (!NT_SUCCESS(status)) {
goto cleanupExit;
}
if (OnDebugPath) {
SetDeviceFlag(deviceExtension, PCMCIA_FDO_ON_DEBUG_PATH);
}
//
// Get the pci interface for reading/writing to config header space
//
status = PcmciaGetInterface(Pdo,
&GUID_BUS_INTERFACE_STANDARD,
sizeof(BUS_INTERFACE_STANDARD),
(PINTERFACE) &deviceExtension->PciBusInterface);
if (!NT_SUCCESS(status)) {
goto cleanupExit;
}
//
// Make sure IRQ routing is to isa. This has come up when installing from a pcmcia CD-rom.
// What happens is that, after we start booting GUI mode, the controller is at first
// in legacy PCIC mode (set by the bios to accomplish boot). At some point, the _INIT
// method is run, and we switch to cardbus mode. So, under the following conditions:
// 1) irq routing bit is still off
// 2) cd-rom is asserting its interrupt
// 3) host controller routes cd-rom interrupt to PCI
// then we hang.
//
GetPciConfigSpace(deviceExtension, CFGSPACE_BRIDGE_CTRL, &word, 2);
word |= BCTRL_IRQROUTING_ENABLE;
SetPciConfigSpace(deviceExtension, CFGSPACE_BRIDGE_CTRL, &word, 2);
if (!(PcmciaGlobalFlags & PCMCIA_DISABLE_ACPI_NAMESPACE_CHECK)) {
status = PcmciaGetInterface(fdo,
&GUID_ACPI_INTERFACE_STANDARD,
sizeof(ACPI_INTERFACE_STANDARD),
(PINTERFACE) &AcpiInterface
);
if (NT_SUCCESS(status)) {
SetFdoFlag(deviceExtension, PCMCIA_FDO_IN_ACPI_NAMESPACE);
}
}
}
//
// Get settings from registry (potentially also compatible ControllerType)
//
PcmciaGetControllerRegistrySettings(deviceExtension);
if ((deviceExtension->ControllerType == PcmciaInvalidControllerType)) {
//
// not really sure what this is... maybe it's a PNP0E00 on a boot. Just do
// the least common denominator
//
PcmciaSetControllerType(deviceExtension, PcmciaIntelCompatible);
}
//
// Link this fdo to the list of fdo's managed by the driver
//
DebugPrint((PCMCIA_DEBUG_PNP, "FDO %08X now linked to fdolist by AddDevice\n", fdo));
deviceExtension->NextFdo = FdoList;
FdoList = fdo;
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
cleanupExit:
MarkDeviceDeleted(deviceExtension);
//
// Cannot support a controller without knowing its type etc.
//
if (deviceExtension->LinkName.Buffer) {
IoDeleteSymbolicLink(&deviceExtension->LinkName);
ExFreePool(deviceExtension->LinkName.Buffer);
}
if (deviceExtension->LowerDevice) {
IoDetachDevice(deviceExtension->LowerDevice);
}
IoDeleteDevice(fdo);
return status;
}
NTSTATUS
PcmciaCreateFdo(
IN PDRIVER_OBJECT DriverObject,
OUT PDEVICE_OBJECT *NewDeviceObject
)
/*++
Routine Description:
This routine will create and initialize a functional device object to
be attached to a Pcmcia controller PDO.
Arguments:
DriverObject - a pointer to the driver object this is created under
DeviceObject - a location to store the pointer to the new device object
Return Value:
STATUS_SUCCESS if everything was successful
reason for failure otherwise
--*/
{
UCHAR deviceNameBuffer[64];
LONG deviceNumber = -1;
ANSI_STRING ansiDeviceName;
UNICODE_STRING unicodeDeviceName;
UNICODE_STRING unicodeLinkName;
PDEVICE_OBJECT deviceObject = NULL;
PFDO_EXTENSION deviceExtension = NULL;
BOOLEAN LinkCreated = FALSE;
ULONG count;
NTSTATUS status=STATUS_SUCCESS;
PAGED_CODE();
//
// Zero out allocated memory pointers so we know if they must be freed
//
RtlZeroMemory(&ansiDeviceName, sizeof(ANSI_STRING));
RtlZeroMemory(&unicodeDeviceName, sizeof(UNICODE_STRING));
RtlZeroMemory(&unicodeLinkName, sizeof(UNICODE_STRING));
//
// Run in a loop incrementing the device number count until we either
// get an error or find a name that's not taken
//
try {
do {
//
// free buffer from previous loop
//
if (unicodeDeviceName.Buffer != NULL) {
RtlFreeUnicodeString(&unicodeDeviceName);
unicodeDeviceName.Buffer = NULL;
}
//
// create the device name
//
sprintf(deviceNameBuffer, "%s%d", PCMCIA_DEVICE_NAME, ++deviceNumber);
RtlInitAnsiString(&ansiDeviceName, deviceNameBuffer);
status = RtlAnsiStringToUnicodeString(
&unicodeDeviceName,
&ansiDeviceName,
TRUE);
if (!NT_SUCCESS(status)) {
DebugPrint((PCMCIA_DEBUG_FAIL, "PcmciaCreateFdo: Error creating unicode "
"string [%#08lx]\n",
status));
leave;
}
//
// create the device object
//
status = IoCreateDevice(DriverObject,
sizeof(FDO_EXTENSION),
&unicodeDeviceName,
FILE_DEVICE_CONTROLLER,
0L,
FALSE,
&deviceObject);
} while ((status == STATUS_OBJECT_NAME_EXISTS) ||
(status == STATUS_OBJECT_NAME_COLLISION));
if (!NT_SUCCESS(status)) {
DebugPrint((PCMCIA_DEBUG_FAIL, "PcmciaCreateFdo: Error creating device object "
"[%#08lx]\n",
status));
leave;
}
// Setup symbolic link for VDDs
//
//
// create the link name (reuse the device name buffers for this)
//
sprintf(deviceNameBuffer,"%s%d", PCMCIA_LINK_NAME, deviceNumber);
RtlInitAnsiString(&ansiDeviceName, deviceNameBuffer);
status = RtlAnsiStringToUnicodeString(
&unicodeLinkName,
&ansiDeviceName,
TRUE);
if (!NT_SUCCESS(status)) {
DebugPrint((PCMCIA_DEBUG_FAIL, "PcmciaCreateFdo: Error creating unicode "
"string [%#08lx]\n",
status));
leave;
}
status = IoCreateSymbolicLink(&unicodeLinkName, &unicodeDeviceName);
if (!NT_SUCCESS(status)) {
DebugPrint((PCMCIA_DEBUG_FAIL, "PcmciaCreateFdo: Symbolic Link was not created\n"));
leave;
}
LinkCreated = TRUE;
//
// Set up the device extension.
//
deviceExtension = deviceObject->DeviceExtension;
deviceExtension->DeviceObject = deviceObject;
deviceExtension->LinkName = unicodeLinkName;
deviceExtension->RegistryPath = DriverRegistryPath;
deviceExtension->DriverObject = DriverObject;
deviceExtension->DeviceObject = deviceObject;
deviceExtension->PdoList = NULL;
deviceExtension->LivePdoCount = 0;
deviceExtension->Flags = PCMCIA_FDO_OFFLINE;
deviceExtension->WaitWakeState= WAKESTATE_DISARMED;
//
// Lock for synching device access
//
PCMCIA_INITIALIZE_DEVICE_LOCK(deviceExtension);
*NewDeviceObject = deviceObject;
} finally {
DebugPrint((PCMCIA_DEBUG_INFO, "PcmciaCreateFdo: Cleanup\n"));
//
//
// deallocate temporary objects
//
if (unicodeDeviceName.Buffer != NULL) {
RtlFreeUnicodeString(&unicodeDeviceName);
}
//
// destroy objects if there was an error
//
if (!NT_SUCCESS(status)) {
if (LinkCreated) {
IoDeleteSymbolicLink(&unicodeLinkName);
}
if (unicodeLinkName.Buffer != NULL) {
RtlFreeUnicodeString(&unicodeLinkName);
}
if (deviceObject != NULL) {
MarkDeviceDeleted(deviceExtension);
IoDeleteDevice(deviceObject);
}
}
}
return status;
}
NTSTATUS
PcmciaStartPcmciaController(
IN PDEVICE_OBJECT Fdo
)
/*++
Routine Description:
Arguments:
Return value:
--*/
{
PFDO_EXTENSION deviceExtension = Fdo->DeviceExtension;
PSOCKET socket;
BOOLEAN sharedInterrupt;
KINTERRUPT_MODE interruptMode;
NTSTATUS status;
INTERFACE_TYPE interfaceType;
//
// Now the controller registers should be accessible
//
deviceExtension->Flags &= ~PCMCIA_FDO_OFFLINE;
//
// Set up the socket list
//
if (!deviceExtension->SocketList) {
if (CardBusExtension(deviceExtension)) {
status = CBBuildSocketList(deviceExtension);
} else {
switch (deviceExtension->ControllerType) {
case PcmciaIntelCompatible:
case PcmciaElcController:
case PcmciaCLPD6729:
case PcmciaPciPcmciaBridge:
case PcmciaNEC98:
case PcmciaNEC98102: {
status = PcicBuildSocketList(deviceExtension);
break;
}
case PcmciaDatabook: {
status = TcicBuildSocketList(deviceExtension);
break;
}
default:
status = STATUS_UNSUCCESSFUL;
}
}
if (!NT_SUCCESS(status)) {
return status;
}
}
//
// Get the IRQ mask for the controller. This is based on several
// values in the registry.
//
PcmciaGetRegistryFdoIrqMask(deviceExtension);
deviceExtension->SystemPowerState = PowerSystemWorking;
deviceExtension->DevicePowerState = PowerDeviceD0;
//
// Initialize our DpcForIsr
//
IoInitializeDpcRequest(Fdo, PcmciaInterruptDpc);
//
// Initialize socket objects
//
for (socket = deviceExtension->SocketList; socket; socket = socket->NextSocket) {
socket->PdoList = NULL;
//
// Initialize the ready enable event.
//
KeInitializeEvent(&socket->PCCardReadyEvent,
SynchronizationEvent,
FALSE
);
//
// Initialize power objects
//
KeInitializeTimer(&socket->PowerTimer);
KeInitializeDpc(&socket->PowerDpc, PcmciaSocketPowerWorker, socket);
socket->FdoIrq = deviceExtension->Configuration.Interrupt.u.Interrupt.Vector;
}
//
// Assume we are going to poll
//
deviceExtension->Flags |= PCMCIA_USE_POLLED_CSC;
deviceExtension->PcmciaInterruptObject = NULL;
if ((deviceExtension->Configuration.Interrupt.u.Interrupt.Level != 0) &&
CardBusExtension(deviceExtension) &&
!(PcmciaGlobalFlags & PCMCIA_GLOBAL_FORCE_POLL_MODE)) {
//
// Hook up the controller interrupt for detecting pc-card plug ins/outs
//
interruptMode=((deviceExtension->Configuration.Interrupt.Flags & CM_RESOURCE_INTERRUPT_LATCHED) == CM_RESOURCE_INTERRUPT_LATCHED) ? Latched:LevelSensitive;
sharedInterrupt=(deviceExtension->Configuration.Interrupt.ShareDisposition == CmResourceShareShared)?
TRUE:FALSE;
status = IoConnectInterrupt(&(deviceExtension->PcmciaInterruptObject),
(PKSERVICE_ROUTINE) PcmciaInterrupt,
(PVOID) Fdo,
NULL,
deviceExtension->Configuration.TranslatedInterrupt.u.Interrupt.Vector,
(KIRQL) deviceExtension->Configuration.TranslatedInterrupt.u.Interrupt.Level,
(KIRQL) deviceExtension->Configuration.TranslatedInterrupt.u.Interrupt.Level,
interruptMode,
sharedInterrupt,
(KAFFINITY) deviceExtension->Configuration.TranslatedInterrupt.u.Interrupt.Affinity,
FALSE);
if (!NT_SUCCESS(status)) {
DebugPrint((PCMCIA_DEBUG_FAIL, "Unable to connect interrupt\n"));
} else {
//
// We connected. Turn off poll mode
//
deviceExtension->Flags &= ~PCMCIA_USE_POLLED_CSC;
}
}
status = PcmciaInitializeController(Fdo);
if (!NT_SUCCESS(status)) {
DebugPrint((PCMCIA_DEBUG_FAIL, "PcmciaStartAdapter: PcmciaInitializeFdo failed\n"));
return status;
}
if (deviceExtension->Flags & PCMCIA_USE_POLLED_CSC) {
LARGE_INTEGER dueTime;
KeInitializeDpc(&deviceExtension->TimerDpc, PcmciaTimerDpc, deviceExtension->DeviceObject);
KeInitializeTimer(&deviceExtension->PollTimer);
//
// Set first fire to twice the peroidic interval - just
//
dueTime.QuadPart = -PCMCIA_CSC_POLL_INTERVAL * 1000 * 10 * 2;
KeSetTimerEx(&(deviceExtension->PollTimer),
dueTime,
PCMCIA_CSC_POLL_INTERVAL,
&deviceExtension->TimerDpc
);
}
return STATUS_SUCCESS;
}
NTSTATUS
PcmciaInitializeController(
IN PDEVICE_OBJECT Fdo
)
/*++
Routine Description:
Initializes the pcmcia controller
Arguments:
Fdo - pointer to the device object for the controller
Return value:
STATUS_SUCCESS - if initialization is successful
--*/
{
PFDO_EXTENSION fdoExtension=Fdo->DeviceExtension;
PSOCKET socket;
NTSTATUS status = STATUS_SUCCESS;
//
// do vendor-specific init of controller
//
if (DeviceDispatchTable[fdoExtension->DeviceDispatchIndex].InitController) {
(*DeviceDispatchTable[fdoExtension->DeviceDispatchIndex].InitController)(fdoExtension);
}
//
// If LegacyIrqMask is not filled in, put a generic mask there in case we need it
//
if (fdoExtension->LegacyIrqMask == 0) {
fdoExtension->LegacyIrqMask = (USHORT)(*(fdoExtension->SocketList->SocketFnPtr->PCBGetIrqMask))(fdoExtension);
}
fdoExtension->LegacyIrqMask &= ~globalFilterIrqMask;
for (socket = fdoExtension->SocketList; socket; socket = socket->NextSocket) {
if (!(*(socket->SocketFnPtr->PCBInitializePcmciaSocket))(socket)) {
status = STATUS_UNSUCCESSFUL;
break;
}
PcmciaGetSocketStatus(socket);
PcmciaSetZV(fdoExtension, socket, FALSE);
ResetSocketFlag(socket, SOCKET_ENABLED_FOR_CARD_DETECT);
if (!(fdoExtension->Flags & PCMCIA_USE_POLLED_CSC) && (socket->FdoIrq != 0)) {
if ((*(socket->SocketFnPtr->PCBEnableDisableCardDetectEvent))(socket, TRUE)) {
//
// card detect event was successfully enabled
//
SetSocketFlag(socket, SOCKET_ENABLED_FOR_CARD_DETECT);
} else {
DebugPrint((PCMCIA_DEBUG_FAIL, "fdo %x failed to enable card detect event on IRQ %x\n",
Fdo, fdoExtension->Configuration.Interrupt.u.Interrupt.Vector));
}
}
}
return status;
}
VOID
PcmciaTimerDpc(
IN PKDPC Dpc,
IN PDEVICE_OBJECT DeviceObject,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
/*++
Routine Description
This routine polls for card insertions or deletions
for the given PCMCIA controller. If a card status change
condition is detected, it invokes the appropriate DPC
to process the card arrival/departure.
Arguments
Dpc - Pointer to the Dpc object
DeviceObject - Pointer to the FDO of the PCMCIA controller that should be polled
Return Value
None
--*/
{
PFDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
PSOCKET socket;
BOOLEAN isCardInSocket, wasCardInSocket, callDpc = FALSE;
if (fdoExtension->Flags & PCMCIA_FDO_OFFLINE) {
return;
}
if (!ValidateController(fdoExtension)) {
//
// Something is wrong with the controller, hopefully it is just
// temporary. For now, do nothing
//
return;
}
for (socket = fdoExtension->SocketList; socket; socket = socket->NextSocket) {
isCardInSocket = (*(socket->SocketFnPtr->PCBDetectCardInSocket))(socket);
wasCardInSocket = IsCardInSocket(socket);
if (isCardInSocket != wasCardInSocket) {
DebugPrint((PCMCIA_DEBUG_INFO, "PcmciaTimerDpc: Setting socket %x change interrupt, cardInSocket=%x Socket->Flags=%x\n", socket, isCardInSocket, socket->Flags));
SetSocketFlag(socket, SOCKET_CHANGE_INTERRUPT);
callDpc = TRUE;
}
}
if (callDpc) {
LARGE_INTEGER dueTime;
KeCancelTimer(&fdoExtension->EventTimer);
dueTime.QuadPart = -((LONG)EventDpcDelay*10);
KeSetTimer(&fdoExtension->EventTimer, dueTime, &fdoExtension->EventDpc);
}
}
BOOLEAN
PcmciaInterrupt(
IN PKINTERRUPT InterruptObject,
PVOID Context
)
/*++
Routine Description:
interrupt handler
Arguments:
InterruptObject - Pointer to the interrupt object.
Context - Pointer to the device context.
Return Value:
Status
--*/
{
PFDO_EXTENSION deviceExtension;
PSOCKET socket;
BOOLEAN statusChanged = FALSE;
deviceExtension=((PDEVICE_OBJECT)Context)->DeviceExtension;
if (deviceExtension->Flags & PCMCIA_FDO_OFFLINE) {
return FALSE;
}
if (!ValidateController(deviceExtension)) {
//
// the controller is broken in some way. Treat like a spurious int
//
ASSERT(FALSE);
return FALSE;
}
//
// Interrupted because of a card removal, or a card insertion.
//
for (socket = deviceExtension->SocketList; socket; socket = socket->NextSocket) {
//
// Check if the card status changed
//
if ((*(socket->SocketFnPtr->PCBDetectCardChanged))(socket)) {
DebugPrint((PCMCIA_DEBUG_ISR, "skt %x card change\n", socket));
if (deviceExtension->DevicePowerState == PowerDeviceD0) {
SetSocketFlag(socket, SOCKET_CHANGE_INTERRUPT);
}
statusChanged = TRUE;
}
socket->ReadyChanged = (*(socket->SocketFnPtr->PCBDetectReadyChanged))(socket);
if (socket->ReadyChanged) {
DebugPrint((PCMCIA_DEBUG_ISR, "skt %x Ready changed\n", socket));
statusChanged = TRUE;
}
//
// Clear card status interrupt, probably leftover from wait/wake
//
if ((socket->SocketFnPtr->PCBDetectCardStatus)) {
(*(socket->SocketFnPtr->PCBDetectCardStatus))(socket);
}
}
if (statusChanged && (deviceExtension->DevicePowerState == PowerDeviceD0)) {
//
// Something changed out there.. could be
// a card insertion/removal.
// Request a DPC to check it out.
//
IoRequestDpc((PDEVICE_OBJECT) Context, NULL, NULL);
}
return statusChanged;
}
VOID
PcmciaInterruptDpc(
IN PKDPC Dpc,
IN PDEVICE_OBJECT DeviceObject,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
/*++
Routine Description:
This DPC is just an intermediate step in getting to the main DPC
handler. This is used to "debounce" hardware and give it some time after
the physical interrupt has come in.
Arguments:
DeviceObject - Pointer to the device object.
Return Value:
--*/
{
PFDO_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
LARGE_INTEGER dueTime;
KeCancelTimer(&deviceExtension->EventTimer);
dueTime.QuadPart = -((LONG)EventDpcDelay*10);
KeSetTimer(&deviceExtension->EventTimer, dueTime, &deviceExtension->EventDpc);
}
VOID
PcmciaDpc(
IN PKDPC Dpc,
IN PDEVICE_OBJECT DeviceObject,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
/*++
Routine Description:
This deferred procedure will be called due to a request for DPC
from the interrupt routine. The device object passed contains
information concerning which sockets have changed. Search this
list and free/clean up any sockets that used to have PCCards.
Arguments:
DeviceObject - Pointer to the device object.
Return Value:
--*/
{
PFDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
PSOCKET socket;
if (fdoExtension->Flags & PCMCIA_FDO_OFFLINE) {
return;
}
DebugPrint((PCMCIA_DEBUG_DPC, "PcmciaDpc: Card Status Change DPC entered...\n"));
//
// For synchronization with the enumeration & removal
// routines which have a tendency to pop off pdo's
// etc
//
PCMCIA_ACQUIRE_DEVICE_LOCK_AT_DPC_LEVEL(fdoExtension);
for (socket = fdoExtension->SocketList; socket; socket = socket->NextSocket) {
if (socket->ReadyChanged) {
KeSetEvent(&socket->PCCardReadyEvent, 0, FALSE);
}
if (IsSocketFlagSet(socket,SOCKET_CHANGE_INTERRUPT)) {
DebugPrint((PCMCIA_DEBUG_DPC, "PcmciaDpc: Socket %x has SOCKET_CHANGE_INTERRUPT set\n", socket));
//
// This socket has changed status
//
ResetSocketFlag(socket, SOCKET_CHANGE_INTERRUPT);
ResetSocketFlag(socket, SOCKET_SUPPORT_MESSAGE_SENT);
SetSocketFlag(socket, SOCKET_CARD_STATUS_CHANGE);
if ((*(socket->SocketFnPtr->PCBDetectCardInSocket))(socket)) {
SetSocketFlag(socket, SOCKET_INSERTED_SOUND_PENDING);
//
// Assume we have a single function card here.
// This will be corrected by when we parse the tuple data (for an R2 card)
// or PCI returns more than one PDO (for a cardbus card)
//
socket->NumberOfFunctions = 1;
ResetSocketFlag(socket, SOCKET_CARD_MULTIFUNCTION);
//
// If we get a physical plug in, then we better clean up even if we didn't
// get the remove yet.
//
ResetSocketFlag(socket, SOCKET_CLEANUP_PENDING);
ResetSocketFlag(socket, SOCKET_ENUMERATE_PENDING);
} else {
if (IsSocketFlagSet(socket, SOCKET_REMOVED_SOUND_PENDING)) {
ResetSocketFlag(socket, SOCKET_REMOVED_SOUND_PENDING);
PcmciaPlaySound(CARD_REMOVED_SOUND);
}
if (socket->PdoList) {
PPDO_EXTENSION pdoExtension;
PDEVICE_OBJECT pdo, nextPdo;
//
// Mark all the pdo's which hang off this socket (more than one possible only
// if this is a Multifunction PC-Card)
//
for (pdo = socket->PdoList; pdo!=NULL; pdo=nextPdo) {
pdoExtension = pdo->DeviceExtension;
nextPdo = pdoExtension->NextPdoInSocket;
pdoExtension->NextPdoInSocket = NULL;
MarkDevicePhysicallyRemoved(pdoExtension);
}
socket->PdoList = NULL;
}
//
// Hack for Topic95 controllers staying in 3.3v
//
if (fdoExtension->ControllerType == PcmciaTopic95) {
ULONG state = CBReadSocketRegister(socket, CARDBUS_SOCKET_PRESENT_STATE_REG);
if ((state & CARDBUS_CB_CARD) && !(state & (CARDBUS_CD1 | CARDBUS_CD2))) {
state &= ~(SKTFORCE_CBCARD | SKTFORCE_3VCARD);
CBWriteSocketRegister(socket, CARDBUS_SOCKET_FORCE_EVENT_REG, state);
}
}
//
// Clear power requirements
//
socket->Vcc = socket->Vpp1 = socket->Vpp2 = 0;
//
// Make sure i/o arbiter is not hanging on the devnode
//
if (CardBusExtension(fdoExtension)) {
IoInvalidateDeviceState(fdoExtension->Pdo);
}
}
}
}
PCMCIA_RELEASE_DEVICE_LOCK_FROM_DPC_LEVEL(fdoExtension);
IoInvalidateDeviceRelations(fdoExtension->Pdo, BusRelations);
return;
}
VOID
PcmciaSetControllerType(
IN PFDO_EXTENSION FdoExtension,
IN PCMCIA_CONTROLLER_TYPE ControllerType
)
/*++
Routine Description
This routine does the housekeeping for setting the controller type,
and the corresponding device index.
Arguments
FdoExtension - Pointer to device extension for the pcmcia controller
ControllerType - new controller type to set into the extension
Return Value
None. Must succeed.
--*/
{
PCMCIA_CONTROLLER_CLASS ctlClass;
ULONG index;
FdoExtension->ControllerType = ControllerType;
ctlClass = PcmciaClassFromControllerType(FdoExtension->ControllerType);
//
// first assume cardbus
//
MarkDeviceCardBus(FdoExtension);
switch(ctlClass) {
case PcmciaIntelCompatible:
case PcmciaPciPcmciaBridge:
case PcmciaElcController:
case PcmciaDatabook:
case PcmciaNEC_98:
MarkDevice16Bit(FdoExtension);
break;
case PcmciaCirrusLogic:
if (ControllerType == PcmciaCLPD6729) {
MarkDevice16Bit(FdoExtension);
}
break;
}
//
// Look up the device in our dispatch table
//
for (index = 0; DeviceDispatchTable[index].ControllerClass != PcmciaInvalidControllerClass; index++) {
if (DeviceDispatchTable[index].ControllerClass == ctlClass) {
FdoExtension->DeviceDispatchIndex = index;
break;
}
}
FdoExtension->Flags &= ~PCMCIA_MEMORY_24BIT;
if (!HasWindowPageRegister(FdoExtension)) {
FdoExtension->Flags |= PCMCIA_MEMORY_24BIT;
}
if ((FdoExtension->InterfaceType == InterfaceTypeUndefined) ||
(FdoExtension->InterfaceType == Isa)) {
FdoExtension->Flags |= PCMCIA_MEMORY_24BIT;
}
switch(ControllerType) {
case PcmciaTI1031:
case PcmciaTI1130:
//
// restrict memory ranges for PDO's to 24bit
// (because of missing "window page" functionality).
//
FdoExtension->Flags |= PCMCIA_MEMORY_24BIT;
break;
}
DebugPrint((PCMCIA_DEBUG_INFO, "fdo %08x Controller Type %x\n",
FdoExtension->DeviceObject, ControllerType));
}
NTSTATUS
PcmciaGetPciControllerType(
IN PDEVICE_OBJECT Pdo,
IN PDEVICE_OBJECT Fdo
)
/*++
Routine Description:
Look at the PCI hardware ID to see if it is already a device we know about. If so,
set the appropriate controller type in the fdoExtension.
Arguments:
Pdo - Physical Device object for the Pcmcia controller owned by the PCI driver
Fdo - Functional Device object for the pcmcia controller owned by this driver, whose
extension will store the relevant controller information upon exit from this routine.
Return Value:
STATUS_SUCCESS Things are fine and information obtained
STATUS_NOT_SUPPORTED This is actually a healthy status for this routine: all it means
is that this PDO is not on a PCI bus, so no information needs to be
obtained anyways.
Any other status Failure. Caller probably needs to back out & not support this controller
--*/
{
PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
PIRP irp;
IO_STATUS_BLOCK statusBlock;
PIO_STACK_LOCATION irpSp;
PCI_COMMON_CONFIG pciConfig;
PPCI_CONTROLLER_INFORMATION id;
PPCI_VENDOR_INFORMATION vid;
KEVENT event;
NTSTATUS status;
BOOLEAN foundController = FALSE;
PAGED_CODE();
//
// Allocate & initialize an Irp (IRP_MN_READ_CONFIG) to be sent down
// to the PCI bus driver to get config. header for this controller
//
// Following is all standard stuff to send an IRP down - needs no documentation
//
// Fresh PDO. No need to jump through hoops to get attached devices
//
KeInitializeEvent (&event, NotificationEvent, FALSE);
irp = IoBuildSynchronousFsdRequest( IRP_MJ_PNP,
Pdo,
NULL,
0,
0,
&event,
&statusBlock
);
if (irp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
irp->IoStatus.Information = 0;
irpSp = IoGetNextIrpStackLocation(irp);
irpSp->MinorFunction = IRP_MN_READ_CONFIG;
irpSp->Parameters.ReadWriteConfig.WhichSpace = PCI_WHICHSPACE_CONFIG;
irpSp->Parameters.ReadWriteConfig.Buffer = &pciConfig;
irpSp->Parameters.ReadWriteConfig.Offset = 0;
irpSp->Parameters.ReadWriteConfig.Length = sizeof(pciConfig);
status = IoCallDriver(Pdo, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = statusBlock.Status;
}
if (!NT_SUCCESS(status)) {
return status;
}
//
// Now weed out the critical information from the config header and
// store it away in the fdo's extension
//
if (pciConfig.SubClass == PCI_SUBCLASS_BR_PCMCIA) {
PcmciaSetControllerType(fdoExtension, PcmciaPciPcmciaBridge);
} else if (pciConfig.SubClass == PCI_SUBCLASS_BR_CARDBUS) {
PcmciaSetControllerType(fdoExtension, PcmciaCardBusCompatible);
} else {
//
// Unknown controller
//
return STATUS_UNSUCCESSFUL;
}
//
// Look up the PCI device id in our table
//
for (id = (PPCI_CONTROLLER_INFORMATION) PciControllerInformation;id->VendorID != PCI_INVALID_VENDORID; id++) {
if ((id->VendorID == pciConfig.VendorID) && (id->DeviceID == pciConfig.DeviceID)) {
PcmciaSetControllerType(fdoExtension, id->ControllerType);
foundController = TRUE;
break;
}
}
//
// Didn't find a specific vendor/device id, try to just base it on the vendor id
//
if (!foundController) {
for (vid = (PPCI_VENDOR_INFORMATION) PciVendorInformation;vid->VendorID != PCI_INVALID_VENDORID; vid++) {
if (vid->VendorID == pciConfig.VendorID) {
PcmciaSetControllerType(fdoExtension, vid->ControllerClass);
break;
}
}
}
return STATUS_SUCCESS;
}