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

1593 lines
48 KiB
C
Raw 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:
fdopnp.c
Abstract:
This module contains the code that handles PNP irps for pcmcia bus driver
targeted towards the FDO's (for the pcmcia controller object)
Author:
Ravisankar Pudipeddi (ravisp) Oct 15 1996
Neil Sandlin (neilsa) June 1 1999
Environment:
Kernel mode
Revision History :
--*/
#include "pch.h"
//
// Internal References
//
NTSTATUS
PcmciaFdoFilterResourceRequirements(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp
);
NTSTATUS
PcmciaFdoGetHardwareIds(
IN PDEVICE_OBJECT Fdo,
OUT PUNICODE_STRING HardwareIds
);
NTSTATUS
PcmciaFdoStartDevice(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp,
OUT BOOLEAN *PassedDown,
OUT BOOLEAN *NeedsRecompletion
);
NTSTATUS
PcmciaFdoStopDevice(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp,
OUT BOOLEAN *PassedDown,
OUT BOOLEAN *NeedsRecompletion
);
NTSTATUS
PcmciaFdoRemoveDevice(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp
);
VOID
PcmciaCleanupFdo(
IN PFDO_EXTENSION FdoExtension
);
NTSTATUS
PcmciaFdoDeviceCapabilities(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp,
OUT BOOLEAN *PassedDown,
OUT BOOLEAN *NeedsRecompletion
);
NTSTATUS
PcmciaAreCardBusCardsSupported(
IN PFDO_EXTENSION FdoExtension
);
NTSTATUS
PcmciaFdoGetAssignedResources(
IN PCM_RESOURCE_LIST ResourceList,
IN PCM_RESOURCE_LIST TranslatedResourceList,
IN PFDO_EXTENSION DeviceExtension
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PcmciaFdoPnpDispatch)
#pragma alloc_text(PAGE, PcmciaFdoGetHardwareIds)
#pragma alloc_text(PAGE, PcmciaFdoStartDevice)
#pragma alloc_text(PAGE, PcmciaFdoStopDevice)
#pragma alloc_text(PAGE, PcmciaFdoRemoveDevice)
#pragma alloc_text(PAGE, PcmciaFdoFilterResourceRequirements)
#pragma alloc_text(PAGE, PcmciaFdoGetAssignedResources)
#pragma alloc_text(PAGE, PcmciaFdoDeviceCapabilities)
#pragma alloc_text(PAGE, PcmciaAreCardBusCardsSupported)
#endif
NTSTATUS
PcmciaFdoPnpDispatch (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
PNP/Power IRPs dispatch routine for the PCMCIA bus controller
Arguments:
DeviceObject - Pointer to the device object.
Irp - Pointer to the IRP
Return Value:
Status
--*/
{
PIO_STACK_LOCATION nextIrpStack;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PFDO_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
NTSTATUS status = Irp->IoStatus.Status;
BOOLEAN PassedDown = FALSE;
BOOLEAN NeedsReCompletion = FALSE;
PAGED_CODE();
#if DBG
if (irpStack->MinorFunction > IRP_MN_PNP_MAXIMUM_FUNCTION) {
DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x irp %08x - Unknown PNP irp\n",
DeviceObject, irpStack->MinorFunction));
} else {
DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x irp %08x --> %s\n",
DeviceObject, Irp, PNP_IRP_STRING(irpStack->MinorFunction)));
}
#endif
switch (irpStack->MinorFunction) {
case IRP_MN_START_DEVICE: {
status = PcmciaFdoStartDevice(DeviceObject,
Irp,
&PassedDown,
&NeedsReCompletion);
break;
}
case IRP_MN_QUERY_STOP_DEVICE: {
status = STATUS_SUCCESS;
break;
}
case IRP_MN_CANCEL_STOP_DEVICE: {
status = STATUS_SUCCESS;
break;
}
case IRP_MN_STOP_DEVICE: {
status = PcmciaFdoStopDevice(DeviceObject,
Irp,
&PassedDown,
&NeedsReCompletion);
break;
}
case IRP_MN_QUERY_DEVICE_RELATIONS: {
//
// Return the list of devices on the bus
//
status = PcmciaDeviceRelations(
DeviceObject,
Irp,
irpStack->Parameters.QueryDeviceRelations.Type,
(PDEVICE_RELATIONS *) &Irp->IoStatus.Information
);
break;
}
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: {
status = PcmciaFdoFilterResourceRequirements(DeviceObject, Irp);
PassedDown = TRUE;
NeedsReCompletion = TRUE;
break;
}
case IRP_MN_QUERY_REMOVE_DEVICE: {
status = STATUS_SUCCESS;
break;
}
case IRP_MN_CANCEL_REMOVE_DEVICE: {
status = STATUS_SUCCESS;
break;
}
case IRP_MN_REMOVE_DEVICE:{
status = PcmciaFdoRemoveDevice(DeviceObject, Irp);
PassedDown = TRUE ;
NeedsReCompletion = TRUE ;
break;
}
case IRP_MN_SURPRISE_REMOVAL: {
PcmciaFdoStopDevice(DeviceObject, NULL, NULL, NULL);
status = STATUS_SUCCESS;
break;
}
case IRP_MN_QUERY_ID: {
UNICODE_STRING unicodeId;
if (deviceExtension->Flags & PCMCIA_DEVICE_LEGACY_DETECTED) {
RtlInitUnicodeString(&unicodeId, NULL);
switch (irpStack->Parameters.QueryId.IdType) {
case BusQueryHardwareIDs: {
DebugPrint((PCMCIA_DEBUG_INFO, " Hardware Ids for fdo %x\n", DeviceObject));
status = PcmciaFdoGetHardwareIds(DeviceObject, &unicodeId);
if (NT_SUCCESS(status)) {
Irp->IoStatus.Information = (ULONG_PTR) unicodeId.Buffer;
}
break;
}
}
}
break;
}
case IRP_MN_QUERY_CAPABILITIES: {
status = PcmciaFdoDeviceCapabilities(DeviceObject,
Irp,
&PassedDown,
&NeedsReCompletion);
break;
}
case IRP_MN_QUERY_LEGACY_BUS_INFORMATION:
//
// If this FDO represents a CardBus bridge, we pass this irp down
// to the PCI PDO which will fill in the PCI bus type and number,
// otherwise we fail the IRP.
//
if (!CardBusExtension(deviceExtension)) {
status = STATUS_NOT_IMPLEMENTED;
}
//
// if status is still STATUS_NOT_SUPPORTED, then later code will pass
// this irp down the stack.
//
break;
default: {
DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x irp %08x - Skipping unsupported irp\n", DeviceObject, Irp));
break;
}
}
if (!PassedDown) {
//
// Set the IRP status only if we set it to something other than
// STATUS_NOT_SUPPORTED.
//
if (status != STATUS_NOT_SUPPORTED) {
Irp->IoStatus.Status = status ;
}
//
// Pass down if success or STATUS_NOT_SUPPORTED. Otherwise, Complete.
//
if (NT_SUCCESS(status) || (status == STATUS_NOT_SUPPORTED)) {
DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x irp %08x pass %s %08x\n",
DeviceObject, Irp,
STATUS_STRING(Irp->IoStatus.Status), Irp->IoStatus.Status));
//
// Below macro fills status with return of IoCallDriver. It does
// not change the Irps status in any way.
//
PcmciaSkipCallLowerDriver(status, deviceExtension->LowerDevice, Irp);
} else {
DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x irp %08x comp %s %08x\n",
DeviceObject, Irp,
STATUS_STRING(status), status));
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
} else if (NeedsReCompletion) {
//
// Set the IRP status only if we set it to something other than
// STATUS_NOT_SUPPORTED.
//
if (status != STATUS_NOT_SUPPORTED) {
Irp->IoStatus.Status = status ;
}
status = Irp->IoStatus.Status ;
DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x irp %08x comp %s %08x\n",
DeviceObject, Irp,
STATUS_STRING(status), status));
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
return status ;
}
NTSTATUS
PcmciaFdoDeviceCapabilities(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp,
OUT BOOLEAN *PassedDown,
OUT BOOLEAN *NeedsRecompletion
)
/*++
Routine Description
Records the device capabilities of this pcmcia controller,
so 1. they can be used in the power management for the controller
and 2. they can be used for determining the capabilities of the
child pc-card PDO's of this pcmcia controller.
If this is legacy detected pcmcia controller (ISA-based), the pdo for
the pcmcia controller is a dummy madeup device - hence the capabilities
are filled in by ourselves.
Otherwise, the capabilities are obtained by sending down the Irp
to the parent bus.
In either case, the capabilities are cached in the device extension of
the pcmcia controller for future use.
Arguments
Fdo - Pointer to functional device object of the pcmcia
controller
Irp - Pointer to the i/o request packet
PassedDown - Contains FALSE on entry, which means caller must
complete or pass down irp based on status. If set
to TRUE, Irp may need to be re-completed...
NeedsRecompletion - ...In which case this parameter will be checked
Return Value
STATUS_SUCCESS Capabilities returned
STATUS_INSUFFICIENT_RESOURCES Could not allocate memory to cache the capabilities
--*/
{
PFDO_EXTENSION fdoExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_CAPABILITIES capabilities;
NTSTATUS status;
PAGED_CODE();
capabilities = irpStack->Parameters.DeviceCapabilities.Capabilities;
fdoExtension = Fdo->DeviceExtension;
if (fdoExtension->Flags & PCMCIA_DEVICE_LEGACY_DETECTED) {
//
// This is a madeup devnode (ISA based PCMCIA controller).
// Fill in the capabilities ourselves
//
RtlZeroMemory(capabilities,
sizeof(DEVICE_CAPABILITIES));
//
// Non removable, non ejectable
//
capabilities->Removable = FALSE;
capabilities->UniqueID = FALSE;
capabilities->EjectSupported = FALSE;
//
// Address & number need work..
//
capabilities->Address = -1;
capabilities->UINumber = -1;
//
// We cannot power down this controller
//
capabilities->DeviceState[PowerSystemSleeping1] = PowerDeviceD0;
capabilities->DeviceState[PowerSystemSleeping2] = PowerDeviceD0;
capabilities->DeviceState[PowerSystemSleeping3] = PowerDeviceD0;
capabilities->DeviceState[PowerSystemHibernate] = PowerDeviceD3;
//
// Obviously wake is not supported on this legacy detected
// piece of xxxx
//
capabilities->SystemWake = PowerSystemUnspecified;
capabilities->DeviceWake = PowerDeviceUnspecified;
capabilities->D1Latency = 0;
capabilities->D2Latency = 0;
capabilities->D3Latency = 0;
status = STATUS_SUCCESS;
} else {
//
// Either a PCI-PCMCIA bridge or PCI-Cardbus bridge
// Send this down the stack to obtain the capabilities
//
ASSERT (fdoExtension->LowerDevice != NULL);
status = PcmciaIoCallDriverSynchronous(fdoExtension->LowerDevice, Irp);
*PassedDown = TRUE ;
*NeedsRecompletion = TRUE ;
}
if (NT_SUCCESS(status)) {
//
// NOTE: HACKHACK:
//
// Here we provide an option to override the device wake of the pcmcia controller.
// There are several controllers, notably TI 12xx controllers, which say they
// can wake from D3, but really can effectively only do WOL from D2. That's because they
// turn of socket power when put into D3. They fixed this on the TI 14xx line.
//
// So here we update the device wake field, and potentially the device states from the BIOS.
// Note that this has to be used carefully, and only override a particular BIOS's settings
// where it has been verified the device still works at the lower (more awake) device state.
//
if (PcmciaControllerDeviceWake) {
if (PcmciaControllerDeviceWake < capabilities->DeviceWake) {
capabilities->DeviceWake = PcmciaControllerDeviceWake;
}
if (PcmciaControllerDeviceWake < capabilities->DeviceState[PowerSystemSleeping1]) {
capabilities->DeviceState[PowerSystemSleeping1] = PcmciaControllerDeviceWake;
}
if (PcmciaControllerDeviceWake < capabilities->DeviceState[PowerSystemSleeping2]) {
capabilities->DeviceState[PowerSystemSleeping2] = PcmciaControllerDeviceWake;
}
if (PcmciaControllerDeviceWake < capabilities->DeviceState[PowerSystemSleeping3]) {
capabilities->DeviceState[PowerSystemSleeping3] = PcmciaControllerDeviceWake;
}
}
//
// Cache the device capabilities in the device extension
// for this pcmcia controller.
//
RtlCopyMemory(&fdoExtension->DeviceCapabilities,
capabilities,
sizeof(DEVICE_CAPABILITIES));
} else {
RtlZeroMemory(&fdoExtension->DeviceCapabilities, sizeof(DEVICE_CAPABILITIES));
}
return status;
}
NTSTATUS
PcmciaFdoFilterResourceRequirements(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp
)
/*++
Routine Description
Filters Resource requirements for PCMCIA controllers generated
by the bus driver controlling the PDO for the PCMCIA controller.
Currently adds memory range as an additional resource requirement
since the BAR doesn't specify this
Note for CardBus controllers:
It is necessary to request an attribute memory window here for
reading CIS of 16-bit PC-Cards - we need a 24 bit address for that,
and this is the most elegant way of doing it - instead of
special casing it in PCI driver
Arguments
DeviceExtension - Pointer to extension for the PCMCIA controller in question
IoReqList - Pointer the the original resource requiremnts ejected by
the bus driver
FilteredRequirements - Pointer to the filtered resource req. list will be returned
in this variable
Return Value:
STATUS_SUCCESS if filtering is successful
Any other status - could not filter
--*/
{
PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
PIO_RESOURCE_REQUIREMENTS_LIST oldReqList;
PIO_RESOURCE_REQUIREMENTS_LIST newReqList;
PIO_RESOURCE_LIST oldList, newList;
PIO_RESOURCE_DESCRIPTOR ioResourceDesc;
ULONG newReqSize;
ULONG oldlistSize, newlistSize, altListSize;
ULONG index;
ULONG IntCount = 0;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
NTSTATUS status;
PAGED_CODE();
status = PcmciaIoCallDriverSynchronous(fdoExtension->LowerDevice, Irp);
if (!NT_SUCCESS(status)) {
return status;
}
if (Irp->IoStatus.Information == 0) {
oldReqList = irpStack->Parameters.FilterResourceRequirements.IoResourceRequirementList;
if (oldReqList == NULL) {
//
// NULL List, nothing to do
//
return(Irp->IoStatus.Status);
}
} else {
//
// Use the returned list
//
oldReqList = (PIO_RESOURCE_REQUIREMENTS_LIST)Irp->IoStatus.Information;
}
//
// Add an alternative list without the IRQ requirement, if one exists. Model the
// new alternative list after the first list.
//
oldList = oldReqList->List;
ioResourceDesc = oldList->Descriptors;
altListSize = 0;
for (index = 0; index < oldList->Count; index++) {
// Count the descriptors, excluding any IRQ descriptors
if (ioResourceDesc->Type == CmResourceTypeInterrupt) {
IntCount++;
}
ioResourceDesc++;
}
if (IntCount) {
//
// "+1" because we are adding two later, but IO_RESOURCE_LIST already has 1
//
altListSize = sizeof(IO_RESOURCE_LIST) + ((oldList->Count+1)-IntCount)*sizeof(IO_RESOURCE_DESCRIPTOR);
}
//
// Add a memory range requirement to what we already have..
//
newReqSize = oldReqList->ListSize +
oldReqList->AlternativeLists*2*sizeof(IO_RESOURCE_DESCRIPTOR) +
altListSize;
newReqList = ExAllocatePool(PagedPool, newReqSize);
if (newReqList == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(newReqList, oldReqList, FIELD_OFFSET(IO_RESOURCE_REQUIREMENTS_LIST, List));
newReqList->ListSize = newReqSize;
newList = newReqList->List;
oldList = oldReqList->List;
for (index = 0; index < oldReqList->AlternativeLists; index++) {
//
// Compute the size of the current original list
//
oldlistSize = sizeof(IO_RESOURCE_LIST) + (oldList->Count-1) * sizeof(IO_RESOURCE_DESCRIPTOR);
newlistSize = oldlistSize;
RtlCopyMemory(newList, oldList, newlistSize);
//
// Add memory requirement
//
ioResourceDesc = (PIO_RESOURCE_DESCRIPTOR) (((PUCHAR) newList) + newlistSize);
ioResourceDesc->Option = IO_RESOURCE_PREFERRED;
ioResourceDesc->Type = CmResourceTypeMemory;
ioResourceDesc->ShareDisposition = CmResourceShareDeviceExclusive;
ioResourceDesc->Flags = CM_RESOURCE_MEMORY_READ_WRITE;
ioResourceDesc->u.Memory.MinimumAddress.QuadPart = fdoExtension->AttributeMemoryLow;
ioResourceDesc->u.Memory.MaximumAddress.QuadPart = fdoExtension->AttributeMemoryHigh;
ioResourceDesc->u.Memory.Length = fdoExtension->AttributeMemorySize;
ioResourceDesc->u.Memory.Alignment = fdoExtension->AttributeMemoryAlignment;
ioResourceDesc++;
//
// The other - less restrictive - alternative.
//
ioResourceDesc->Option = IO_RESOURCE_ALTERNATIVE;
ioResourceDesc->Type = CmResourceTypeMemory;
ioResourceDesc->ShareDisposition = CmResourceShareDeviceExclusive;
ioResourceDesc->Flags = CM_RESOURCE_MEMORY_READ_WRITE;
ioResourceDesc->u.Memory.MinimumAddress.QuadPart = 0;
if ((fdoExtension->Flags & PCMCIA_MEMORY_24BIT) == 0) {
ioResourceDesc->u.Memory.MaximumAddress.QuadPart = 0xFFFFFFFF;
} else {
ioResourceDesc->u.Memory.MaximumAddress.QuadPart = 0xFFFFFF;
}
ioResourceDesc->u.Memory.Length = fdoExtension->AttributeMemorySize;
ioResourceDesc->u.Memory.Alignment = fdoExtension->AttributeMemoryAlignment;
newList->Count += 2;
newlistSize += 2*sizeof(IO_RESOURCE_DESCRIPTOR);
oldList = (PIO_RESOURCE_LIST) (((PUCHAR) oldList) + oldlistSize);
newList = (PIO_RESOURCE_LIST) (((PUCHAR) newList) + newlistSize);
}
if (altListSize != 0) {
PIO_RESOURCE_DESCRIPTOR oldResourceDesc;
//
// Here we add the alternate list which doesn't contain an IRQ requirement.
// Note that we use the first "new list" as the "old list". This way, we
// pick up the things we added in the previous loop. All we have to do is
// copy every descriptor except for the interrupt descriptor.
//
// Note: newList is still set from previous loop
//
oldList = newReqList->List;
//
// First copy the basic structure without the descriptors
//
RtlCopyMemory(newList, oldList, sizeof(IO_RESOURCE_LIST) - sizeof(IO_RESOURCE_DESCRIPTOR));
oldResourceDesc = oldList->Descriptors;
ioResourceDesc = newList->Descriptors;
for (index = 0; index < oldList->Count; index++) {
if (oldResourceDesc->Type != CmResourceTypeInterrupt) {
*ioResourceDesc = *oldResourceDesc;
ioResourceDesc++;
} else {
//
// We've deleted a descriptor
//
newList->Count--;
}
oldResourceDesc++;
}
newReqList->AlternativeLists++;
}
Irp->IoStatus.Information = (ULONG_PTR) newReqList;
irpStack->Parameters.FilterResourceRequirements.IoResourceRequirementList =
newReqList;
//
// Free up the old resource reqs
//
ExFreePool(oldReqList);
fdoExtension->Flags |= PCMCIA_FILTER_ADDED_MEMORY;
return STATUS_SUCCESS;
}
NTSTATUS
PcmciaFdoGetHardwareIds(
IN PDEVICE_OBJECT Fdo,
OUT PUNICODE_STRING HardwareIds
)
/*++
Routine description:
This routine returns the hardware ids for the given 'legacy' pcmcia controller
NOTE: this routine is required only for pcmcia controllers detected by this driver
itself and registered via IoReportDetectedDevice.
Arguments:
Fdo - Pointer to the functional device object representing the pcmcia controller
CompatibleIds - Pointer to the unicode string which would contain the hardware ids
as a multi-string on return
Return value:
STATUS_SUCCESS
Any other status - could not generate compatible ids
--*/
{
PCSTR strings[2];
PCMCIA_CONTROLLER_TYPE controllerType;
ULONG count, index;
BOOLEAN found;
PAGED_CODE();
controllerType = ((PFDO_EXTENSION)Fdo->DeviceExtension)->ControllerType;
found = FALSE;
for (index = 0; (PcmciaAdapterHardwareIds[index].ControllerType != PcmciaInvalidControllerType); index++) {
if (PcmciaAdapterHardwareIds[index].ControllerType == controllerType) {
found = TRUE;
break;
}
}
if (found) {
strings[0] = PcmciaAdapterHardwareIds[index].Id;
} else {
DebugPrint((PCMCIA_DEBUG_INFO, "PcmciaGetAdapterHardwareIds: Could not find find hardware id for %x, controllerType %x\n",
Fdo,
controllerType
));
strings[0] = "";
}
count = 1;
return PcmciaStringsToMultiString(strings,
count,
HardwareIds);
}
NTSTATUS
PcmciaFdoStartDevice(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp,
OUT BOOLEAN *PassedDown,
OUT BOOLEAN *NeedsRecompletion
)
/*++
Routine Description:
This routine will start the PCMCIA controller with the supplied
resources. The IRP is sent down to the pdo first, so PCI or ISAPNP
or whoever sits underneath gets a chance to program the controller
to decode the resources.
Arguments:
Fdo - Functional device object of the PCMCIA controller
Irp - Well, it's the start irp, yah?
PassedDown - Contains FALSE on entry, which means caller must
complete or pass down irp based on status. If set
to TRUE, Irp may need to be re-completed...
NeedsRecompletion - ...In which case this parameter will be checked
Return value:
Status
--*/
{
NTSTATUS status;
PFDO_EXTENSION deviceExtension = Fdo->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PCM_RESOURCE_LIST resList, translatedResList, newResList, newTranslatedResList;
PAGED_CODE();
if (deviceExtension->Flags & PCMCIA_DEVICE_STARTED) {
//
// Start to already started device
//
DebugPrint((PCMCIA_DEBUG_FAIL,"PcmciaFdoStartDevice: Fdo %x already started\n",
Fdo));
return STATUS_SUCCESS;
}
//
// Parse AllocatedResources & get IoPort/AttributeMemoryBase/IRQ info.
//
status = PcmciaFdoGetAssignedResources(irpStack->Parameters.StartDevice.AllocatedResources,
irpStack->Parameters.StartDevice.AllocatedResourcesTranslated,
deviceExtension
);
if (!NT_SUCCESS(status)) {
//
// Ha. This is most likely a START for a madeup devnode (report-detected legacy PCMCIA controller)
// which has been removed subsequently, hence not reported again with proper resources.
// We return an appropriate status
//
DebugPrint((PCMCIA_DEBUG_FAIL, "Pcmcia: No resources assigned to FDO, probably bogus START for"
"non-existent controller\n" ));
return STATUS_NO_SUCH_DEVICE;
}
DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x StartAdapter: AttrMem Base %x\n", Fdo, deviceExtension->PhysicalBase));
DebugPrint((PCMCIA_DEBUG_PNP, " VirtualBase %x AttrMem Size %x\n", deviceExtension->AttributeMemoryBase, deviceExtension->AttributeMemorySize));
if (deviceExtension->Flags & PCMCIA_FILTER_ADDED_MEMORY) {
ULONG newSize;
ULONG index;
PCM_PARTIAL_RESOURCE_LIST resPartialList, translatedResPartialList;
PCM_PARTIAL_RESOURCE_DESCRIPTOR resDesc, translatedResDesc;
PCM_PARTIAL_RESOURCE_DESCRIPTOR newResDesc, newTranslatedResDesc;
//
// We need to remove the memory resource requirement
//
resList= irpStack->Parameters.StartDevice.AllocatedResources;
resPartialList = &resList->List[0].PartialResourceList;
translatedResList= irpStack->Parameters.StartDevice.AllocatedResourcesTranslated;
translatedResPartialList = &translatedResList->List[0].PartialResourceList;
newSize = sizeof(CM_RESOURCE_LIST) + (resPartialList->Count-2)*sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
newResList = ExAllocatePool(PagedPool,
newSize);
if (newResList == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
newTranslatedResList = ExAllocatePool(PagedPool,
newSize);
if (newTranslatedResList == NULL) {
ExFreePool(newResList);
return STATUS_INSUFFICIENT_RESOURCES;
}
newResList->Count = newTranslatedResList->Count = 1;
newResList->List[0].InterfaceType = resList->List[0].InterfaceType;
newTranslatedResList->List[0].InterfaceType = translatedResList->List[0].InterfaceType;
newResList->List[0].BusNumber = resList->List[0].BusNumber;
newTranslatedResList->List[0].BusNumber = translatedResList->List[0].BusNumber;
newResList->List[0].PartialResourceList.Version = resPartialList->Version;
newResList->List[0].PartialResourceList.Revision = resPartialList->Revision;
newResList->List[0].PartialResourceList.Count = resPartialList->Count - 1;
newTranslatedResList->List[0].PartialResourceList.Version = translatedResPartialList->Version;
newTranslatedResList->List[0].PartialResourceList.Revision = translatedResPartialList->Revision;
newTranslatedResList->List[0].PartialResourceList.Count = translatedResPartialList->Count - 1;
resDesc = resPartialList->PartialDescriptors;
translatedResDesc = translatedResPartialList->PartialDescriptors;
newResDesc = newResList->List[0].PartialResourceList.PartialDescriptors;
newTranslatedResDesc = newTranslatedResList->List[0].PartialResourceList.PartialDescriptors;
if (CardBusExtension(deviceExtension)) {
//
// Remove last memory descriptor - which is what we added
//
RtlCopyMemory(newResDesc,
resDesc,
newResList->List[0].PartialResourceList.Count * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
RtlCopyMemory(newTranslatedResDesc,
translatedResDesc,
newTranslatedResList->List[0].PartialResourceList.Count * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
} else {
//
// Remove the only memory descriptor..
//
for (index = 0; index < resPartialList->Count;
index++, resDesc++, translatedResDesc++, newResDesc++, newTranslatedResDesc++) {
if (resDesc->Type != CmResourceTypeMemory) {
*newResDesc = *resDesc;
*newTranslatedResDesc = *translatedResDesc;
}
}
}
irpStack->Parameters.StartDevice.AllocatedResources = newResList;
irpStack->Parameters.StartDevice.AllocatedResourcesTranslated = newTranslatedResList;
}
//
// Send this down to the PDO first
//
status = PcmciaIoCallDriverSynchronous(deviceExtension->LowerDevice, Irp);
*PassedDown = TRUE ;
//
// We set this because the completion routine returns
// STATUS_MORE_PROCESSING_REQUIRED, which means it needs to be completed
// again.
//
*NeedsRecompletion = TRUE ;
if (deviceExtension->Flags & PCMCIA_FILTER_ADDED_MEMORY) {
ExFreePool(newResList);
ExFreePool(newTranslatedResList);
irpStack->Parameters.StartDevice.AllocatedResources = resList;
irpStack->Parameters.StartDevice.AllocatedResourcesTranslated = translatedResList;
}
if (!NT_SUCCESS(status)) {
return status;
}
//
// Give the hardware some time to settle after returning from the pdo
//
PcmciaWait(256);
//
// Initialize the hardware
//
status = PcmciaStartPcmciaController(Fdo);
if (NT_SUCCESS(status)) {
deviceExtension->Flags |= PCMCIA_DEVICE_STARTED;
}
//
// Remember if cardbus cards will be supported for this controller
//
if (CardBusExtension(deviceExtension) &&
!PcmciaAreCardBusCardsSupported(deviceExtension)) {
deviceExtension->Flags |= PCMCIA_CARDBUS_NOT_SUPPORTED;
}
return status;
}
NTSTATUS
PcmciaFdoStopDevice(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp OPTIONAL,
OUT BOOLEAN *PassedDown OPTIONAL,
OUT BOOLEAN *NeedsRecompletion OPTIONAL
)
/*++
Routine Description:
IRP_MN_STOP_DEVICE handler for the given pcmcia controller.
If Irp is present, it'll send it down first to the PDO.
Unhooks the interrupt/cancels poll timer etc.
Arguments:
Fdo - Pointer to functional device object for the pcmcia
controller
Irp - If present it's the pointer to the stop Irp initiated
by PnP
PassedDown - Contains FALSE on entry, which means caller must
complete or pass down irp based on status. If set
to TRUE, Irp may need to be re-completed...
NeedsRecompletion - ...In which case this parameter will be checked.
Note: PassedDown and NeedsCompletion are ignored and
optional only if Irp is NULL.
Return value:
STATUS_SUCCESS - Pcmcia controller successfully stopped
Other - Stop failed
--*/
{
PFDO_EXTENSION deviceExtension = Fdo->DeviceExtension;
PSOCKET socket;
NTSTATUS status;
if (!(deviceExtension->Flags & PCMCIA_DEVICE_STARTED)) {
//
// Already stopped
//
return STATUS_SUCCESS;
}
PcmciaFdoDisarmWake(deviceExtension);
//
// Disable the interrupt
//
if (deviceExtension->PcmciaInterruptObject) {
for (socket = deviceExtension->SocketList; socket; socket = socket->NextSocket) {
//
// Disable the controller interrupts
//
(*(socket->SocketFnPtr->PCBEnableDisableCardDetectEvent))(socket, FALSE);
(*(socket->SocketFnPtr->PCBEnableDisableWakeupEvent))(socket, NULL, FALSE);
//
// Apparently IBM ThinkPads like this
//
PcmciaWait(PCMCIA_ENABLE_DELAY);
}
}
//
// the bus driver below us will make us go offline
//
deviceExtension->Flags |= PCMCIA_FDO_OFFLINE;
//
// clear pending event
//
KeCancelTimer(&deviceExtension->EventTimer);
//
// Send this down to the PDO
//
if (ARGUMENT_PRESENT(Irp)) {
status = PcmciaIoCallDriverSynchronous(deviceExtension->LowerDevice, Irp);
*PassedDown = TRUE ;
*NeedsRecompletion = TRUE ;
if (!NT_SUCCESS(status)) {
return status;
}
}
if (deviceExtension->Flags & PCMCIA_USE_POLLED_CSC) {
//
// cancel the card status change poller
//
KeCancelTimer(&deviceExtension->PollTimer);
deviceExtension->Flags &= ~PCMCIA_USE_POLLED_CSC;
}
if (deviceExtension->PcmciaInterruptObject) {
//
// unhook the interrupt
//
IoDisconnectInterrupt(deviceExtension->PcmciaInterruptObject);
deviceExtension->PcmciaInterruptObject = NULL;
}
//
// Unmap any i/o space or memory we might have mapped
//
if (deviceExtension->Flags & PCMCIA_ATTRIBUTE_MEMORY_MAPPED) {
MmUnmapIoSpace(deviceExtension->AttributeMemoryBase,
deviceExtension->AttributeMemorySize);
deviceExtension->Flags &= ~PCMCIA_ATTRIBUTE_MEMORY_MAPPED;
deviceExtension->AttributeMemoryBase = 0;
deviceExtension->AttributeMemorySize = 0;
}
if (deviceExtension->Flags & PCMCIA_SOCKET_REGISTER_BASE_MAPPED) {
MmUnmapIoSpace(deviceExtension->CardBusSocketRegisterBase,
deviceExtension->CardBusSocketRegisterSize);
deviceExtension->Flags &= ~PCMCIA_SOCKET_REGISTER_BASE_MAPPED;
deviceExtension->CardBusSocketRegisterBase = 0;
deviceExtension->CardBusSocketRegisterSize = 0;
}
deviceExtension->Flags &= ~PCMCIA_DEVICE_STARTED;
return STATUS_SUCCESS;
}
NTSTATUS
PcmciaFdoRemoveDevice(
IN PDEVICE_OBJECT Fdo,
IN PIRP Irp
)
/*++
Routine Description:
Handles IRP_MN_REMOVE for the pcmcia controller.
Stops the adapter if it isn't already, sends the IRP
to the PDO first & cleans up the Fdo for this controller
and detaches & deletes the device object.
Arguments:
Fdo - Pointer to functional device object for the controller
to be removed
Return value:
Status
--*/
{
PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
PDEVICE_OBJECT pdo, nextPdo, fdo, prevFdo;
PPDO_EXTENSION pdoExtension;
NTSTATUS status;
UNREFERENCED_PARAMETER(Irp);
if (fdoExtension->Flags & PCMCIA_DEVICE_STARTED) {
//
// Stop the fdo first.
//
PcmciaFdoStopDevice(Fdo, NULL, NULL, NULL);
}
//
// Send this down to the PDO
//
status = PcmciaIoCallDriverSynchronous(fdoExtension->LowerDevice, Irp);
if (!NT_SUCCESS(status)) {
return status;
}
//
// If the PdoList in the fdoExtension is non-empty it means:
// that the PDOs in the list were not physically removed, but
// a soft REMOVE was issued, hence they are still hanging on
// and now this controller itself is being REMOVED.
// Hence we dispose of those PDOs now
//
for (pdo = fdoExtension->PdoList; pdo != NULL ; pdo = nextPdo) {
DebugPrint((PCMCIA_DEBUG_INFO,
"RemoveDevice: pdo %x child of fdo %x was not removed before fdo\n",
pdo, Fdo));
pdoExtension = pdo->DeviceExtension;
ASSERT (!IsDevicePhysicallyRemoved(pdoExtension));
//
// It's possible for this bit to be on, if the device was added,
// but never started (because of some other error.
//ASSERT (!IsDeviceAlive(pdoExtension));
nextPdo = pdoExtension->NextPdoInFdoChain;
if (!IsDeviceDeleted(pdoExtension)) {
MarkDeviceDeleted(pdoExtension);
PcmciaCleanupPdo(pdo);
IoDeleteDevice(pdo);
}
}
MarkDeviceDeleted(fdoExtension);
PcmciaCleanupFdo(fdoExtension);
//
// Remove this from the fdo list..
//
prevFdo = NULL;
for (fdo = FdoList; fdo != NULL; prevFdo = fdo, fdo = fdoExtension->NextFdo) {
fdoExtension = fdo->DeviceExtension;
if (fdo == Fdo) {
if (prevFdo) {
//
// Delink this fdo
//
((PFDO_EXTENSION)prevFdo->DeviceExtension)->NextFdo
= fdoExtension->NextFdo;
} else {
FdoList = fdoExtension->NextFdo;
}
break;
}
}
DebugPrint((PCMCIA_DEBUG_PNP, "fdo %08x Remove detach & delete\n", Fdo));
IoDetachDevice(((PFDO_EXTENSION)Fdo->DeviceExtension)->LowerDevice);
IoDeleteDevice(Fdo);
return STATUS_SUCCESS;
}
VOID
PcmciaCleanupContext(
IN PPCMCIA_CONTEXT pContext
)
/*++
Routine Description
Frees up allocated pool associated with a specific controller
register context.
Arguments
pContext - pointer to a PCMCIA_CONTEXT structure
Return value
none
--*/
{
pContext->RangeCount = 0;
if (pContext->Range) {
ExFreePool(pContext->Range);
pContext->Range = NULL;
}
}
VOID
PcmciaCleanupFdo(
IN PFDO_EXTENSION FdoExtension
)
/*++
Routine Description
Frees up allocated pool, deletes symbolic links etc. for the
associated FDO for the pcmcia controller which is to be removed.
Arguments
FdoExtension - Pointer to the device extension for the FDO of the pcmcia controller
which is being removed
Return value
none
--*/
{
PSOCKET socket, nextSocket;
//
// Free the controller register context
//
PcmciaCleanupContext(&FdoExtension->PciContext);
if (FdoExtension->PciContextBuffer) {
ExFreePool(FdoExtension->PciContextBuffer);
FdoExtension->PciContextBuffer = NULL;
}
PcmciaCleanupContext(&FdoExtension->CardbusContext);
PcmciaCleanupContext(&FdoExtension->ExcaContext);
//
// Delete symbolic links to this fdo
//
if (FdoExtension->LinkName.Buffer != NULL) {
IoDeleteSymbolicLink(&FdoExtension->LinkName);
RtlFreeUnicodeString(&FdoExtension->LinkName);
}
//
// Cleanup the socket structures
//
for (socket = FdoExtension->SocketList; socket != NULL; socket = nextSocket) {
if (socket->CardbusContextBuffer) {
ExFreePool(socket->CardbusContextBuffer);
socket->CardbusContextBuffer = NULL;
}
if (socket->ExcaContextBuffer) {
ExFreePool(socket->ExcaContextBuffer);
socket->ExcaContextBuffer = NULL;
}
nextSocket = socket->NextSocket;
ExFreePool(socket);
}
FdoExtension->SocketList = NULL;
return;
}
NTSTATUS
PcmciaFdoGetAssignedResources(
IN PCM_RESOURCE_LIST ResourceList,
IN PCM_RESOURCE_LIST TranslatedResourceList,
IN PFDO_EXTENSION DeviceExtension
)
/*++
Routine Description:
Extracts the assigned resources to the pcmcia controller
Arguments:
ResourceList - Raw resource list
TranslatedResourceList
DeviceExtension - Device extension of the PCMCIA controller
Return value:
STATUS_SUCCESS
STATUS_UNSUCCESSFUL if resources are not right or enough.
--*/
{
PCM_FULL_RESOURCE_DESCRIPTOR fullResourceDesc;
PCM_PARTIAL_RESOURCE_LIST partialResourceList;
PCM_PARTIAL_RESOURCE_DESCRIPTOR partialResourceDesc;
ULONG addressSpace;
PHYSICAL_ADDRESS physicalAddress;
PHYSICAL_ADDRESS translatedAddress;
ULONG i;
ULONG attributeIndex;
PULONG devicePrivate;
BOOLEAN translated;
PAGED_CODE();
if ((ResourceList == NULL) || (ResourceList->Count <=0) ) {
return STATUS_UNSUCCESSFUL;
}
if (CardBusExtension(DeviceExtension)) {
fullResourceDesc=&TranslatedResourceList->List[0];
DeviceExtension->Configuration.InterfaceType = fullResourceDesc->InterfaceType;
DeviceExtension->Configuration.BusNumber = fullResourceDesc->BusNumber;
partialResourceList = &fullResourceDesc->PartialResourceList;
partialResourceDesc = partialResourceList->PartialDescriptors;
//
// We need just the exca register base
// According to PeterJ the first descriptor
// for us is the cardbus socket register base.
// We trust him.
for (i=0; (i < partialResourceList->Count) && (partialResourceDesc->Type != CmResourceTypeMemory);
i++, partialResourceDesc++);
if (i >= partialResourceList->Count) {
return STATUS_UNSUCCESSFUL;
};
//
// This is memory. We need to map it
//
DeviceExtension->CardBusSocketRegisterBase = MmMapIoSpace(partialResourceDesc->u.Memory.Start,
partialResourceDesc->u.Memory.Length,
FALSE);
DeviceExtension->CardBusSocketRegisterSize = partialResourceDesc->u.Memory.Length;
DeviceExtension->Flags |= PCMCIA_SOCKET_REGISTER_BASE_MAPPED;
//
// Last BAR is attribute memory window. This will be peeled off later.
// It might be a good idea to tack a device private on at the end to
// confirm this. However how do we guarantee our device private does
// not contain the same data as somebody else's? PnP is lacking here -
// we need some convention so that devices can set uniquely identifying stuff
// in there - like maybe the device object they own - to identify it is
// theirs. Till then this should do.
//
if (i > (partialResourceList->Count - 2)) {
//
// No more resources? Bail out.
//
return STATUS_INSUFFICIENT_RESOURCES;
}
for (i++, partialResourceDesc++; (i < (partialResourceList->Count - 1));i++,partialResourceDesc++);
//
// partialResourceDesc points to the last descriptor
//
ASSERT (partialResourceDesc->Type == CmResourceTypeMemory);
DeviceExtension->PhysicalBase = partialResourceDesc->u.Memory.Start;
//
// This is memory. We need to map it
//
DeviceExtension->AttributeMemoryBase = MmMapIoSpace(partialResourceDesc->u.Memory.Start,
partialResourceDesc->u.Memory.Length,
FALSE);
DeviceExtension->AttributeMemorySize = partialResourceDesc->u.Memory.Length;
DeviceExtension->Flags |= PCMCIA_ATTRIBUTE_MEMORY_MAPPED;
//
// Finally see if an IRQ is assigned
//
for (i = 0, partialResourceDesc = partialResourceList->PartialDescriptors;
(i < partialResourceList->Count) && (partialResourceDesc->Type != CmResourceTypeInterrupt);
i++,partialResourceDesc++);
if (i < partialResourceList->Count) {
//
// We have an interrupt to used for CSC
// PCI will ensure that this interrupt is exactly the
// same as the one assigned to the functional interrupt
// for a cardbus pc-card in this controller's socket
//
DebugPrint((PCMCIA_DEBUG_INFO, "PcmciaGetAssignedResources: Interrupt resource assigned\n"));
DeviceExtension->Configuration.TranslatedInterrupt = *partialResourceDesc;
//
// Get the raw interrupt resource - needed to enable the interrupt on the controller
//
fullResourceDesc=&ResourceList->List[0];
partialResourceList = &fullResourceDesc->PartialResourceList;
partialResourceDesc = partialResourceList->PartialDescriptors;
for (i=0; (i< partialResourceList->Count) && (partialResourceDesc->Type != CmResourceTypeInterrupt);
i++, partialResourceDesc++);
if (i < partialResourceList->Count) {
DeviceExtension->Configuration.Interrupt = *partialResourceDesc;
} else {
//
// Should not happen.. translated descriptor was present, but raw is missing!
// Just reset the translated interrupt and pretend no interrupt was assigned
//
RtlZeroMemory(&DeviceExtension->Configuration.TranslatedInterrupt, sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
}
}
} else {
//
// 16-bit pcmcia controller
//
fullResourceDesc=&ResourceList->List[0];
DeviceExtension->Configuration.InterfaceType = fullResourceDesc->InterfaceType;
DeviceExtension->Configuration.BusNumber = fullResourceDesc->BusNumber;
partialResourceList = &fullResourceDesc->PartialResourceList;
partialResourceDesc = partialResourceList->PartialDescriptors;
for (i=0; i<partialResourceList->Count; i++, partialResourceDesc++) {
devicePrivate = partialResourceDesc->u.DevicePrivate.Data;
switch (partialResourceDesc->Type) {
case CmResourceTypeInterrupt: {
if (DeviceExtension->ControllerType != PcmciaCLPD6729 &&
DeviceExtension->ControllerType != PcmciaPciPcmciaBridge &&
DeviceExtension->ControllerType != PcmciaNEC98 &&
DeviceExtension->ControllerType != PcmciaNEC98102) {
// We always poll for Cirrus Logic PCI to PCMCIA controllers
// and other PCI-PCMCIA bridges
//
DeviceExtension->Configuration.Interrupt = *partialResourceDesc;
}
break;
}
case CmResourceTypePort: {
DeviceExtension->Configuration.UntranslatedPortAddress = (USHORT) partialResourceDesc->u.Port.Start.QuadPart;
DeviceExtension->Configuration.PortSize = (USHORT) partialResourceDesc->u.Port.Length;
break;
}
case CmResourceTypeMemory: {
DeviceExtension->PhysicalBase = partialResourceDesc->u.Memory.Start;
DeviceExtension->AttributeMemorySize = partialResourceDesc->u.Memory.Length;
attributeIndex = i;
break;
}
}
}
if ((DeviceExtension->PhysicalBase.QuadPart == 0) ||
(DeviceExtension->Configuration.UntranslatedPortAddress == 0)) {
DebugPrint((PCMCIA_DEBUG_FAIL, "PcmciaGetAssignedResources: Need both memory and i/o for pcmcia controller 0x%X\n",
DeviceExtension->DeviceObject));
return STATUS_INSUFFICIENT_RESOURCES;
}
fullResourceDesc=&TranslatedResourceList->List[0];
partialResourceList = &fullResourceDesc->PartialResourceList;
partialResourceDesc = &partialResourceList->PartialDescriptors[attributeIndex];
switch (partialResourceDesc->Type) {
case CmResourceTypeMemory:
DeviceExtension->AttributeMemoryBase = MmMapIoSpace(partialResourceDesc->u.Memory.Start,
DeviceExtension->AttributeMemorySize,
FALSE);
DeviceExtension->Flags |= PCMCIA_ATTRIBUTE_MEMORY_MAPPED;
break;
case CmResourceTypePort:
DeviceExtension->AttributeMemoryBase = (PUCHAR)(partialResourceDesc->u.Port.Start.QuadPart);
DeviceExtension->Flags &= ~PCMCIA_ATTRIBUTE_MEMORY_MAPPED;
break;
default:
ASSERT(FALSE);
return STATUS_INSUFFICIENT_RESOURCES;
}
}
return STATUS_SUCCESS;
}
NTSTATUS
PcmciaAreCardBusCardsSupported(
IN PFDO_EXTENSION FdoExtension
)
/*++
Routine Description:
Indicates if cardbus cards will be supported on the given PCMCIA
controller on this system
Currently we support cardbus cards only on:
Machines on which BIOS programmed the bus numbers & IntLine
Arguments:
FdoExtension - Pointer to device extension for the pcmcia controller
Return Value:
TRUE - if cardbus cards are supported
FALSE - if not
--*/
{
UCHAR byte;
PAGED_CODE();
//
// Check int line
//
GetPciConfigSpace(FdoExtension,
CFGSPACE_INT_LINE,
&byte,
1);
if (byte == 0xff) {
return FALSE;
}
//
// Check cardbus bus number
//
GetPciConfigSpace(FdoExtension,
CFGSPACE_CARDBUS_BUSNUM,
&byte,
1);
if (byte == 0) {
return FALSE;
}
//
// Check subordinate bus number
//
GetPciConfigSpace(FdoExtension,
CFGSPACE_SUB_BUSNUM,
&byte,
1);
if (byte == 0) {
return FALSE;
}
//
// All tests passed
//
return TRUE;
}