mirror of https://github.com/tongzx/nt5src
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.
1338 lines
36 KiB
1338 lines
36 KiB
/*++
|
|
|
|
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;
|
|
}
|
|
|