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.
2964 lines
94 KiB
2964 lines
94 KiB
/*++
|
|
|
|
Copyright (c) 1997-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
pdopnp.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code to handle
|
|
the IRP_MJ_PNP dispatches for the PDOs
|
|
enumerated by the PCMCIA bus driver
|
|
|
|
|
|
Authors:
|
|
|
|
Ravisankar Pudipeddi (ravisp)
|
|
Neil Sandlin (neilsa) 1-Jun-1999
|
|
|
|
Environment:
|
|
|
|
Kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
//
|
|
// Internal References
|
|
//
|
|
|
|
NTSTATUS
|
|
PcmciaFilterPcCardResourceRequirements(
|
|
IN PPDO_EXTENSION DeviceExtension,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PcmciaFilterPcCardInterrupts(
|
|
IN PIO_RESOURCE_REQUIREMENTS_LIST oldReqList,
|
|
IN ULONG IrqCount,
|
|
IN ULONG IrqMask,
|
|
OUT PIO_RESOURCE_REQUIREMENTS_LIST * FilteredReqList,
|
|
BOOLEAN RouteIsaToPci
|
|
);
|
|
|
|
VOID
|
|
PcmciaCleanupSocketData(
|
|
IN PSOCKET_DATA SocketData
|
|
);
|
|
|
|
VOID
|
|
PcmciaCleanupSocketConfiguration(
|
|
PPDO_EXTENSION pdoExtension
|
|
);
|
|
|
|
NTSTATUS
|
|
PcmciaPdoDeviceCapabilities(
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PcmciaGetPcCardResourceRequirements(
|
|
IN PPDO_EXTENSION PdoExtension,
|
|
PULONG_PTR Information
|
|
);
|
|
|
|
NTSTATUS
|
|
PcmciaConfigEntriesToResourceListChain(
|
|
PPDO_EXTENSION pdoExtension,
|
|
PCONFIG_LIST ConfigList,
|
|
ULONG configCount,
|
|
PPCMCIA_RESOURCE_CHAIN *ResListChainHead
|
|
);
|
|
|
|
NTSTATUS
|
|
PcmciaMergeResourceChainToList(
|
|
PPCMCIA_RESOURCE_CHAIN ResListChain,
|
|
PIO_RESOURCE_REQUIREMENTS_LIST *GeneratedResourceRequirementsList
|
|
);
|
|
|
|
VOID
|
|
PcmciaFreeResourceChain(
|
|
PPCMCIA_RESOURCE_CHAIN ResListChain
|
|
);
|
|
|
|
NTSTATUS
|
|
PcmciaStartPcCard(
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN PCM_RESOURCE_LIST AllocatedResources,
|
|
IN OUT PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PcmciaStopPcCard(
|
|
IN PDEVICE_OBJECT Pdo
|
|
);
|
|
|
|
NTSTATUS
|
|
PcmciaRemovePcCard(
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
PcmciaMfEnumerateConfigurations(
|
|
IN PPDO_EXTENSION PdoExtension,
|
|
IN PSOCKET_DATA socketData,
|
|
PCONFIG_LIST ConfigList,
|
|
IN ULONG Depth,
|
|
PPCMCIA_RESOURCE_CHAIN *MfResListChain
|
|
);
|
|
|
|
NTSTATUS
|
|
PcmciaMfGetResourceRequirements(
|
|
IN PPDO_EXTENSION PdoExtension,
|
|
PULONG_PTR Information
|
|
);
|
|
|
|
VOID
|
|
PcmciaMfBuildResourceMapInfo(
|
|
IN PPDO_EXTENSION PdoExtension,
|
|
PCONFIG_LIST ConfigList,
|
|
ULONG ConfigCount
|
|
);
|
|
|
|
BOOLEAN
|
|
PcmciaMfCheckForOverlappingRanges(
|
|
PCONFIG_LIST ConfigList,
|
|
LONG ConfigCount
|
|
);
|
|
|
|
NTSTATUS
|
|
PcmciaPdoGetBusInformation(
|
|
IN PPDO_EXTENSION PdoExtension,
|
|
OUT PPNP_BUS_INFORMATION * BusInformation
|
|
);
|
|
|
|
NTSTATUS
|
|
PcmciaQueryDeviceText(
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN OUT PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
PcmciaPdoGetDeviceInfSettings(
|
|
IN PPDO_EXTENSION PdoExtension
|
|
);
|
|
|
|
VOID
|
|
PcmciaPdoSetDeviceIrqRouting(
|
|
IN PPDO_EXTENSION PdoExtension
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, PcmciaPdoPnpDispatch)
|
|
#pragma alloc_text(PAGE, PcmciaPdoGetDeviceInfSettings)
|
|
#pragma alloc_text(PAGE, PcmciaPdoSetDeviceIrqRouting)
|
|
#pragma alloc_text(PAGE, PcmciaFilterPcCardInterrupts)
|
|
#pragma alloc_text(PAGE, PcmciaFilterPcCardResourceRequirements)
|
|
#pragma alloc_text(PAGE, PcmciaQueryDeviceText)
|
|
#pragma alloc_text(PAGE, PcmciaGetPcCardResourceRequirements)
|
|
#pragma alloc_text(PAGE, PcmciaConfigEntriesToResourceListChain)
|
|
#pragma alloc_text(PAGE, PcmciaMergeResourceChainToList)
|
|
#pragma alloc_text(PAGE, PcmciaFreeResourceChain)
|
|
#pragma alloc_text(PAGE, PcmciaPdoGetBusInformation)
|
|
#pragma alloc_text(PAGE, PcmciaStartPcCard)
|
|
#pragma alloc_text(PAGE, PcmciaStopPcCard)
|
|
#pragma alloc_text(PAGE, PcmciaRemovePcCard)
|
|
#pragma alloc_text(PAGE, PcmciaPdoDeviceCapabilities)
|
|
#pragma alloc_text(PAGE, PcmciaPdoDeviceControl)
|
|
#pragma alloc_text(PAGE, PcmciaPdoGetDeviceInfSettings)
|
|
#pragma alloc_text(PAGE, PcmciaMfGetResourceRequirements)
|
|
#pragma alloc_text(PAGE, PcmciaMfEnumerateConfigurations)
|
|
#pragma alloc_text(PAGE, PcmciaMfBuildResourceMapInfo)
|
|
#pragma alloc_text(PAGE, PcmciaMfCheckForOverlappingRanges)
|
|
#endif
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PcmciaPdoPnpDispatch(
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles pnp requests
|
|
for the PDOs.
|
|
|
|
Arguments:
|
|
|
|
Pdo - pointer to the physical device object
|
|
Irp - pointer to the io request packet
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension;
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// CardBus PnP Dispatch
|
|
//
|
|
|
|
if (IsCardBusCard(pdoExtension)) { //
|
|
return PcmciaPdoCardBusPnPDispatch(Pdo, Irp);
|
|
}
|
|
|
|
#if DBG
|
|
if (irpStack->MinorFunction > IRP_MN_PNP_MAXIMUM_FUNCTION) {
|
|
DebugPrint((PCMCIA_DEBUG_PNP, "pdo %08x irp %08x Unknown minor function %x\n",
|
|
Pdo, Irp, irpStack->MinorFunction));
|
|
} else {
|
|
DebugPrint((PCMCIA_DEBUG_PNP, "pdo %08x irp %08x --> %s\n",
|
|
Pdo, Irp, PNP_IRP_STRING(irpStack->MinorFunction)));
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// 16-bit (R2) PcCard PnP Dispatch
|
|
//
|
|
switch (irpStack->MinorFunction) {
|
|
|
|
case IRP_MN_START_DEVICE: {
|
|
status = PcmciaStartPcCard(Pdo, irpStack->Parameters.StartDevice.AllocatedResources, Irp);
|
|
PcmciaDoStartSound(pdoExtension->Socket, status);
|
|
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 = PcmciaStopPcCard(Pdo);
|
|
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 = PcmciaRemovePcCard(Pdo, Irp);
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_SURPRISE_REMOVAL: {
|
|
|
|
PcmciaReleaseSocketPower(pdoExtension, NULL);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_QUERY_ID: {
|
|
|
|
UNICODE_STRING unicodeId;
|
|
|
|
status = Irp->IoStatus.Status;
|
|
RtlInitUnicodeString(&unicodeId, NULL);
|
|
|
|
switch (irpStack->Parameters.QueryId.IdType) {
|
|
|
|
case BusQueryDeviceID: {
|
|
DebugPrint((PCMCIA_DEBUG_INFO, " Device Id for pdo %x\n", Pdo));
|
|
status = PcmciaGetDeviceId(Pdo, PCMCIA_MULTIFUNCTION_PARENT, &unicodeId);
|
|
if (NT_SUCCESS(status)) {
|
|
Irp->IoStatus.Information = (ULONG_PTR) unicodeId.Buffer;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case BusQueryInstanceID: {
|
|
DebugPrint((PCMCIA_DEBUG_INFO, " Instance Id for pdo %x\n", Pdo));
|
|
status = PcmciaGetInstanceId(Pdo, &unicodeId);
|
|
if (NT_SUCCESS(status)) {
|
|
Irp->IoStatus.Information = (ULONG_PTR) unicodeId.Buffer;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case BusQueryHardwareIDs: {
|
|
DebugPrint((PCMCIA_DEBUG_INFO, " Hardware Ids for pdo %x\n", Pdo));
|
|
status = PcmciaGetHardwareIds(Pdo, PCMCIA_MULTIFUNCTION_PARENT, &unicodeId);
|
|
if (NT_SUCCESS(status)) {
|
|
Irp->IoStatus.Information = (ULONG_PTR) unicodeId.Buffer;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case BusQueryCompatibleIDs: {
|
|
DebugPrint((PCMCIA_DEBUG_INFO, " Compatible Ids for pdo %x\n", Pdo));
|
|
status = PcmciaGetCompatibleIds( Pdo, PCMCIA_MULTIFUNCTION_PARENT, &unicodeId);
|
|
if (NT_SUCCESS(status)) {
|
|
Irp->IoStatus.Information = (ULONG_PTR) unicodeId.Buffer;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: {
|
|
//
|
|
// PcmciaGetPcCardResourceRequirements will
|
|
// allocate storage for the resource requirements which will be released
|
|
// by the OS.
|
|
//
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
PcmciaPdoGetDeviceInfSettings(pdoExtension);
|
|
PcmciaPdoSetDeviceIrqRouting(pdoExtension);
|
|
|
|
if (IsDeviceMultifunction(pdoExtension)) {
|
|
status = PcmciaMfGetResourceRequirements(pdoExtension,
|
|
&Irp->IoStatus.Information);
|
|
} else {
|
|
status = PcmciaGetPcCardResourceRequirements(pdoExtension,
|
|
&Irp->IoStatus.Information);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_WRITE_CONFIG:
|
|
case IRP_MN_READ_CONFIG: {
|
|
PFDO_EXTENSION fdoExtension= pdoExtension->Socket->DeviceExtension;
|
|
ULONG whichSpace;
|
|
PVOID buffer;
|
|
ULONG offset;
|
|
ULONG length;
|
|
|
|
whichSpace = irpStack->Parameters.ReadWriteConfig.WhichSpace;
|
|
buffer = irpStack->Parameters.ReadWriteConfig.Buffer;
|
|
offset = irpStack->Parameters.ReadWriteConfig.Offset;
|
|
length = irpStack->Parameters.ReadWriteConfig.Length;
|
|
|
|
if (irpStack->MinorFunction == IRP_MN_READ_CONFIG) {
|
|
status = PcmciaReadWriteCardMemory(Pdo,
|
|
whichSpace,
|
|
buffer,
|
|
offset,
|
|
length,
|
|
TRUE);
|
|
} else {
|
|
status = PcmciaReadWriteCardMemory(Pdo,
|
|
whichSpace,
|
|
buffer,
|
|
offset,
|
|
length,
|
|
FALSE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_QUERY_DEVICE_RELATIONS: {
|
|
|
|
PDEVICE_RELATIONS deviceRelations;
|
|
|
|
if (irpStack->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation) {
|
|
status = Irp->IoStatus.Status;
|
|
break;
|
|
}
|
|
|
|
deviceRelations = ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS));
|
|
if (deviceRelations == NULL) {
|
|
|
|
DebugPrint((PCMCIA_DEBUG_FAIL,
|
|
"PcmciaPdoPnpDispatch:unable to allocate memory for device relations\n"));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
status = ObReferenceObjectByPointer(Pdo,
|
|
0,
|
|
NULL,
|
|
KernelMode);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(deviceRelations);
|
|
break;
|
|
}
|
|
|
|
deviceRelations->Count = 1;
|
|
deviceRelations->Objects[0] = Pdo;
|
|
Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_QUERY_CAPABILITIES: {
|
|
status = PcmciaPdoDeviceCapabilities(Pdo, Irp);
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_QUERY_INTERFACE: {
|
|
status = PcmciaPdoQueryInterface(Pdo, Irp);
|
|
//
|
|
// QueryInterface completes the passed in Irp.
|
|
// So just return immediately.
|
|
//
|
|
return status;
|
|
}
|
|
|
|
case IRP_MN_QUERY_DEVICE_TEXT: {
|
|
|
|
status = PcmciaQueryDeviceText(Pdo, Irp);
|
|
|
|
if (status == STATUS_NOT_SUPPORTED ) {
|
|
//
|
|
// Do not change IRP status if this IRP is
|
|
// not handled
|
|
//
|
|
status = Irp->IoStatus.Status;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: {
|
|
status = PcmciaFilterPcCardResourceRequirements(pdoExtension, Irp);
|
|
break;
|
|
}
|
|
|
|
case IRP_MN_QUERY_BUS_INFORMATION: {
|
|
status = PcmciaPdoGetBusInformation(pdoExtension,
|
|
(PPNP_BUS_INFORMATION *) &Irp->IoStatus.Information);
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
//
|
|
// Retain the status
|
|
//
|
|
DebugPrint((PCMCIA_DEBUG_PNP, "pdo %08x irp %08x Skipping unsupported irp\n", Pdo, Irp));
|
|
status = Irp->IoStatus.Status;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
DebugPrint((PCMCIA_DEBUG_PNP, "pdo %08x irp %08x comp %s %08x\n", Pdo, Irp,
|
|
STATUS_STRING(status), status));
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PcmciaPdoGetBusInformation(
|
|
IN PPDO_EXTENSION PdoExtension,
|
|
OUT PPNP_BUS_INFORMATION * BusInformation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the bus type information for the pc-card.
|
|
Bus type is GUID_BUS_TYPE_PCMCIA(legacy type is PcmciaBus) for R2 cards
|
|
Bus numbers are not implemented for PCMCIA, so it's always 0
|
|
|
|
Arguments:
|
|
|
|
PdoExtension - pointer to device extension for the pc-card
|
|
|
|
BusInformation - pointer to the bus information structure that
|
|
needs to be filled in
|
|
|
|
Return value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PAGED_CODE();
|
|
|
|
*BusInformation = ExAllocatePool(PagedPool, sizeof (PNP_BUS_INFORMATION));
|
|
if (!*BusInformation) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory(&((*BusInformation)->BusTypeGuid),
|
|
&GUID_BUS_TYPE_PCMCIA,
|
|
sizeof(GUID));
|
|
(*BusInformation)->LegacyBusType = PCMCIABus;
|
|
(*BusInformation)->BusNumber = 0;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
PcmciaPdoGetDeviceInfSettings(
|
|
IN PPDO_EXTENSION PdoExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves settings from the INF for this device.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Device extension of the Pc-Card
|
|
|
|
Return value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PSOCKET socket = PdoExtension->Socket;
|
|
UNICODE_STRING KeyName;
|
|
HANDLE instanceHandle;
|
|
UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
|
|
PKEY_VALUE_PARTIAL_INFORMATION value = (PKEY_VALUE_PARTIAL_INFORMATION) buffer;
|
|
ULONG length;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = IoOpenDeviceRegistryKey(PdoExtension->DeviceObject,
|
|
PLUGPLAY_REGKEY_DRIVER,
|
|
KEY_READ,
|
|
&instanceHandle
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Look to see if PcmciaExclusiveIrq is specified
|
|
//
|
|
RtlInitUnicodeString(&KeyName, L"PcmciaExclusiveIrq");
|
|
|
|
status = ZwQueryValueKey(instanceHandle,
|
|
&KeyName,
|
|
KeyValuePartialInformation,
|
|
value,
|
|
sizeof(buffer),
|
|
&length);
|
|
|
|
|
|
//
|
|
// If the key doesn't exist, or zero was specified, it means that
|
|
// routing is ok
|
|
//
|
|
if (NT_SUCCESS(status) && (*(PULONG)(value->Data) != 0)) {
|
|
SetDeviceFlag(PdoExtension, PCMCIA_PDO_EXCLUSIVE_IRQ);
|
|
}
|
|
|
|
//
|
|
// Look to see if PcmciaAutoPowerOff is specified
|
|
//
|
|
RtlInitUnicodeString(&KeyName, L"PcmciaAutoPowerOff");
|
|
|
|
status = ZwQueryValueKey(instanceHandle,
|
|
&KeyName,
|
|
KeyValuePartialInformation,
|
|
value,
|
|
sizeof(buffer),
|
|
&length);
|
|
|
|
|
|
//
|
|
// If zero was specified, then don't automatically cut power on shutdown
|
|
//
|
|
if (NT_SUCCESS(status) && (*(PULONG)(value->Data) == 0)) {
|
|
SetDeviceFlag(socket->DeviceExtension, PCMCIA_FDO_DISABLE_AUTO_POWEROFF);
|
|
}
|
|
|
|
//
|
|
// Look to see if PcmciaEnableAudio is specified
|
|
//
|
|
RtlInitUnicodeString(&KeyName, L"PcmciaEnableAudio");
|
|
|
|
status = ZwQueryValueKey(instanceHandle,
|
|
&KeyName,
|
|
KeyValuePartialInformation,
|
|
value,
|
|
sizeof(buffer),
|
|
&length);
|
|
|
|
|
|
//
|
|
// If zero was specified, then don't automatically cut power on shutdown
|
|
//
|
|
if (NT_SUCCESS(status) && (*(PULONG)(value->Data) != 0)) {
|
|
SetDeviceFlag(PdoExtension, PCMCIA_PDO_ENABLE_AUDIO);
|
|
}
|
|
|
|
ZwClose(instanceHandle);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
PcmciaPdoSetDeviceIrqRouting(
|
|
IN PPDO_EXTENSION PdoExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves settings from the INF for this device.
|
|
|
|
Notes:
|
|
|
|
PcmciaExclusiveIrq in the INF determines how the IRQ for the R2 card will be routed.
|
|
Currently, the following logic is used:
|
|
|
|
if (routing disabled)
|
|
choose either detected or legacy
|
|
else
|
|
if !detected, use FdoIrq
|
|
|
|
So, if routing to PCI at all, then only the PCI IRQ will show up in the IoResList.
|
|
BUT: Another approach would be to use the following logic:
|
|
|
|
if (routing disabled)
|
|
choose either detected or legacy
|
|
else
|
|
*merge detected with FdoIrq*
|
|
|
|
This way we may end up using an exclusive IRQ or routing to PCI, depending on what
|
|
the arbiter has decided. That is the reason why I kept both Detected and Legacy around
|
|
in the FdoExtension, because otherwise it would have made more sense just to choose
|
|
and merge them back when they were generated.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Device extension of the Pc-Card
|
|
|
|
Return value:
|
|
|
|
None (SOCKET structure is updated)
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PSOCKET socket = PdoExtension->Socket;
|
|
PFDO_EXTENSION fdoExtension = socket->DeviceExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
ResetSocketFlag(socket, SOCKET_CB_ROUTE_R2_TO_PCI);
|
|
|
|
//
|
|
// First check the conditions that specify we definitely don't want to route to PCI
|
|
//
|
|
|
|
if (!pcmciaDisableIsaPciRouting &&
|
|
CardBusExtension(fdoExtension) &&
|
|
!IsDeviceFlagSet(PdoExtension, PCMCIA_PDO_EXCLUSIVE_IRQ)) {
|
|
|
|
//
|
|
// Here we know we *could* route to PCI, now determine if we *should*. This takes
|
|
// into account several registry settings, as well as the result from the NtDetect
|
|
// IRQ detection algorithm.
|
|
//
|
|
|
|
|
|
//
|
|
// First check to see if there was an override with specifically targeted this
|
|
// controller. This will take precedence.
|
|
//
|
|
if (IsFdoFlagSet(fdoExtension, PCMCIA_FDO_FORCE_PCI_ROUTING)) {
|
|
SetSocketFlag(socket, SOCKET_CB_ROUTE_R2_TO_PCI);
|
|
}
|
|
else if (IsFdoFlagSet(fdoExtension, PCMCIA_FDO_FORCE_ISA_ROUTING)) {
|
|
ResetSocketFlag(socket, SOCKET_CB_ROUTE_R2_TO_PCI);
|
|
}
|
|
|
|
//
|
|
// Now check to see if the detection algorithm succeeded. We should honor this result,
|
|
// particularly if the map was zero, which tells us there are no ISA IRQs attached.
|
|
//
|
|
else if (IsFdoFlagSet(fdoExtension, PCMCIA_FDO_IRQ_DETECT_COMPLETED)) {
|
|
if (fdoExtension->DetectedIrqMask == 0) {
|
|
SetSocketFlag(socket, SOCKET_CB_ROUTE_R2_TO_PCI);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Now see if there was a general override based on this controller type. This has
|
|
// less precedence than the detection algorithm.
|
|
//
|
|
else if (IsFdoFlagSet(fdoExtension, PCMCIA_FDO_PREFER_PCI_ROUTING)) {
|
|
SetSocketFlag(socket, SOCKET_CB_ROUTE_R2_TO_PCI);
|
|
}
|
|
else if (IsFdoFlagSet(fdoExtension, PCMCIA_FDO_PREFER_ISA_ROUTING)) {
|
|
ResetSocketFlag(socket, SOCKET_CB_ROUTE_R2_TO_PCI);
|
|
}
|
|
|
|
|
|
//
|
|
// Here look to see if the device was found, but the irq detection failed for some
|
|
// reason. With no special registry overrides, we fall back on the global default.
|
|
//
|
|
else if (IsFdoFlagSet(fdoExtension, PCMCIA_FDO_IRQ_DETECT_DEVICE_FOUND)) {
|
|
if (!(PcmciaGlobalFlags & PCMCIA_DEFAULT_ROUTE_R2_TO_ISA)) {
|
|
SetSocketFlag(socket, SOCKET_CB_ROUTE_R2_TO_PCI);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Here ntdetect never saw the device. Maybe we just hot-docked.
|
|
// We will base our decision on if it was in ACPI namespace or not. If it
|
|
// isn't in ACPI namespace, then it probably is not connected to ISA
|
|
//
|
|
else {
|
|
if (!IsFdoFlagSet(fdoExtension, PCMCIA_FDO_IN_ACPI_NAMESPACE)) {
|
|
SetSocketFlag(socket, SOCKET_CB_ROUTE_R2_TO_PCI);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
socket->IrqMask = fdoExtension->DetectedIrqMask ? fdoExtension->DetectedIrqMask : fdoExtension->LegacyIrqMask;
|
|
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "pdo %08x IRQ routing=%s, IRQMask=%08x\n", PdoExtension->DeviceObject,
|
|
IsSocketFlagSet(socket, SOCKET_CB_ROUTE_R2_TO_PCI) ? "PCI" : "ISA",
|
|
socket->IrqMask));
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PcmciaFilterPcCardInterrupts(
|
|
IN PIO_RESOURCE_REQUIREMENTS_LIST oldReqList,
|
|
IN ULONG IrqCount,
|
|
IN ULONG IrqMask,
|
|
OUT PIO_RESOURCE_REQUIREMENTS_LIST * FilteredReqList,
|
|
BOOLEAN RouteIsaToPci
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Filters the interrupt resource requirements for R2 Pc-Cards
|
|
|
|
Arguments:
|
|
|
|
oldReqList - Original, 'raw' resource requirements list
|
|
IrqCount - # of irq's that will be deleted (already computed by caller)
|
|
IrqMask - bit mask which indicates which interrupts are valid
|
|
FilteredReqList - pointer to the filtered requirements list which
|
|
will be filled in by this routine
|
|
|
|
Return value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
PIO_RESOURCE_REQUIREMENTS_LIST newReqList;
|
|
PIO_RESOURCE_LIST oldList, newList;
|
|
ULONG newReqSize;
|
|
ULONG oldlistSize, newlistSize;
|
|
ULONG index, oldIndex, newIndex;
|
|
BOOLEAN irqAlternative;
|
|
|
|
PAGED_CODE();
|
|
//
|
|
// Compute the size of the structure with the offending IRQs removed.
|
|
//
|
|
newReqSize = oldReqList->ListSize - IrqCount*sizeof(IO_RESOURCE_DESCRIPTOR);
|
|
|
|
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;
|
|
|
|
//
|
|
// Loop through each alternative list
|
|
//
|
|
for (index = 0; index < oldReqList->AlternativeLists; index++) {
|
|
newList->Version = oldList->Version;
|
|
newList->Revision = oldList->Revision;
|
|
|
|
irqAlternative = FALSE;
|
|
//
|
|
// Loop through each descriptor in the old list
|
|
//
|
|
for (oldIndex = 0, newIndex = 0; oldIndex < oldList->Count; oldIndex++) {
|
|
|
|
if (oldList->Descriptors[oldIndex].Type == CmResourceTypeInterrupt) {
|
|
|
|
if (RouteIsaToPci) {
|
|
|
|
if (!irqAlternative) {
|
|
//
|
|
// First interrupt found, this will be the one we use to route
|
|
//
|
|
newList->Descriptors[newIndex++] = oldList->Descriptors[oldIndex];
|
|
irqAlternative = TRUE;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Normal case (not routed). Filter out irqs that aren't in our mask
|
|
//
|
|
if ((IrqMask & (1<<oldList->Descriptors[oldIndex].u.Interrupt.MinimumVector)) != 0) {
|
|
//
|
|
// Not a bad interrupt descriptor. Copy the old to the new
|
|
//
|
|
newList->Descriptors[newIndex] = oldList->Descriptors[oldIndex];
|
|
|
|
if (newList->Descriptors[newIndex].Type == CmResourceTypeInterrupt) {
|
|
if (irqAlternative) {
|
|
newList->Descriptors[newIndex].Option = IO_RESOURCE_ALTERNATIVE;
|
|
} else {
|
|
irqAlternative = TRUE;
|
|
newList->Descriptors[newIndex].Option = 0;
|
|
}
|
|
}
|
|
newIndex++;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// Not an interrupt descriptor. Copy the old to the new
|
|
//
|
|
newList->Descriptors[newIndex++] = oldList->Descriptors[oldIndex];
|
|
}
|
|
}
|
|
newList->Count = newIndex;
|
|
oldlistSize = sizeof(IO_RESOURCE_LIST) + (oldList->Count-1) * sizeof(IO_RESOURCE_DESCRIPTOR);
|
|
newlistSize = sizeof(IO_RESOURCE_LIST) + (newList->Count-1) * sizeof(IO_RESOURCE_DESCRIPTOR);
|
|
oldList = (PIO_RESOURCE_LIST) (((PUCHAR) oldList) + oldlistSize);
|
|
newList = (PIO_RESOURCE_LIST) (((PUCHAR) newList) + newlistSize);
|
|
}
|
|
|
|
*FilteredReqList = newReqList;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PcmciaFilterPcCardResourceRequirements(
|
|
IN PPDO_EXTENSION DeviceExtension,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Filters the resource requirements for R2 Pc-Cards
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Device extension of the Pc-Card
|
|
|
|
Return value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
{
|
|
PIO_RESOURCE_REQUIREMENTS_LIST IoReqList;
|
|
PIO_RESOURCE_REQUIREMENTS_LIST newReqList;
|
|
PIO_RESOURCE_LIST ioResourceList;
|
|
PIO_RESOURCE_DESCRIPTOR ioResourceDesc;
|
|
PSOCKET socket;
|
|
PFDO_EXTENSION fdoExtension;
|
|
ULONG index1, index2, len;
|
|
ULONGLONG low, high;
|
|
ULONG IrqsToDeleteCount = 0;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
BOOLEAN RouteIrqFound;
|
|
|
|
PAGED_CODE();
|
|
|
|
IoReqList = (PIO_RESOURCE_REQUIREMENTS_LIST) Irp->IoStatus.Information;
|
|
|
|
if (IoReqList == NULL) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
socket = DeviceExtension->Socket;
|
|
fdoExtension = socket->DeviceExtension;
|
|
|
|
for (index1 = 0, ioResourceList = IoReqList->List;
|
|
index1 < IoReqList->AlternativeLists; index1++) {
|
|
ioResourceDesc = ioResourceList->Descriptors;
|
|
|
|
RouteIrqFound = FALSE;
|
|
|
|
for (index2 = 0 ; index2 < ioResourceList->Count; index2++, ioResourceDesc++) {
|
|
if (ioResourceDesc->Type == CmResourceTypeInterrupt) {
|
|
|
|
if (IsSocketFlagSet(socket, SOCKET_CB_ROUTE_R2_TO_PCI)) {
|
|
|
|
//
|
|
// make sure there is space for just 1 level interrupt requirement
|
|
//
|
|
if (!RouteIrqFound) {
|
|
RouteIrqFound = TRUE;
|
|
ioResourceDesc->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
|
|
ioResourceDesc->Option = 0;
|
|
ioResourceDesc->ShareDisposition = CmResourceShareShared;
|
|
ioResourceDesc->u.Interrupt.MinimumVector = socket->FdoIrq;
|
|
ioResourceDesc->u.Interrupt.MaximumVector = socket->FdoIrq;
|
|
|
|
} else {
|
|
//
|
|
// Don't need any other IRQs in this list
|
|
//
|
|
IrqsToDeleteCount++;
|
|
}
|
|
|
|
} else {
|
|
ASSERT (ioResourceDesc->u.Interrupt.MinimumVector == ioResourceDesc->u.Interrupt.MaximumVector);
|
|
//
|
|
// Look to see if there are IRQ's specified in this IoResList which are
|
|
// not in our mask. If so, we are going to have to create a new IoResList.
|
|
// Keep track of how many
|
|
|
|
if (!(socket->IrqMask & (1<<ioResourceDesc->u.Interrupt.MinimumVector))) {
|
|
IrqsToDeleteCount++;
|
|
}
|
|
}
|
|
|
|
} else if (ioResourceDesc->Type == CmResourceTypePort) {
|
|
//
|
|
// We might want to filter this..
|
|
//
|
|
low = ioResourceDesc->u.Port.MinimumAddress.QuadPart;
|
|
high = ioResourceDesc->u.Port.MaximumAddress.QuadPart;
|
|
len = ioResourceDesc->u.Port.Length;
|
|
|
|
//
|
|
// A set of messy rules to see if we should filter the requirements.
|
|
//
|
|
// We don't filter if:
|
|
// 1. User requested number of ports which are more than
|
|
// we can legally ask with our filtered high & low caps on the
|
|
// requirements.
|
|
// 2. User requested a 'fixed' resource requirement, i.e. a specific
|
|
// set of ports of a specified length.
|
|
// 3. User specified resource requirements range is smaller
|
|
// than the range we would obtain if we filter it. We don't filter
|
|
// in this case because the filtering is supposed to restrict the range
|
|
// not expand it. If user thinks he can restrict better than us,
|
|
// honor it .
|
|
//
|
|
//
|
|
if (len > (fdoExtension->IoHigh - fdoExtension->IoLow + 1)) {
|
|
//
|
|
// Case 1. above.
|
|
// Don't filter this.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
if ((low + len -1) >= high) {
|
|
//
|
|
// Case 2.
|
|
// This is a fixed requirement. Don't filter it
|
|
//
|
|
continue;
|
|
}
|
|
|
|
|
|
if (((ULONG)(high - low)) <= (fdoExtension->IoHigh - fdoExtension->IoLow)) {
|
|
//
|
|
// Case 3.
|
|
// Don't filter this
|
|
//
|
|
continue;
|
|
}
|
|
|
|
if ((ULONG) low < fdoExtension->IoLow) {
|
|
low = (ULONGLONG) fdoExtension->IoLow;
|
|
}
|
|
|
|
if ((ULONG) high > fdoExtension->IoHigh) {
|
|
high = (ULONGLONG) fdoExtension->IoHigh;
|
|
}
|
|
|
|
ioResourceDesc->u.Port.MinimumAddress.QuadPart = low;
|
|
ioResourceDesc->u.Port.MaximumAddress.QuadPart = high;
|
|
|
|
} else if (ioResourceDesc->Type == CmResourceTypeMemory) {
|
|
|
|
//
|
|
// pccard hardware can't handle > 32bit addressing
|
|
//
|
|
ASSERT(ioResourceDesc->u.Memory.MinimumAddress.HighPart == 0);
|
|
ioResourceDesc->u.Memory.MaximumAddress.HighPart = 0;
|
|
|
|
if (fdoExtension->Flags & PCMCIA_MEMORY_24BIT) {
|
|
|
|
ASSERT((ioResourceDesc->u.Memory.MinimumAddress.LowPart & 0xFF000000) == 0);
|
|
ioResourceDesc->u.Memory.MaximumAddress.LowPart &= 0xFFFFFF;
|
|
|
|
}
|
|
|
|
//
|
|
// win2k had a bug where o2micro controllers were marked as 24bit. When
|
|
// that was fixed, an o2micro smart card device with an INF bug suddenly
|
|
// stopped working (because the first bug masked the second). This code
|
|
// fixes their INF.
|
|
//
|
|
if ((ioResourceDesc->Flags & CM_RESOURCE_MEMORY_24) &&
|
|
(ioResourceDesc->u.Memory.MinimumAddress.LowPart > 0xFFFFFF) &&
|
|
(PcmciaClassFromControllerType(fdoExtension->ControllerType) == PcmciaO2Micro)) {
|
|
|
|
ioResourceDesc->u.Memory.MinimumAddress.LowPart &= 0xFFFFFF;
|
|
}
|
|
}
|
|
}
|
|
|
|
ioResourceList = (PIO_RESOURCE_LIST) (((PUCHAR) ioResourceList) +
|
|
sizeof(IO_RESOURCE_LIST) +
|
|
(ioResourceList->Count - 1)* sizeof(IO_RESOURCE_DESCRIPTOR));
|
|
} // outer for loop
|
|
|
|
if (IrqsToDeleteCount) {
|
|
|
|
status = PcmciaFilterPcCardInterrupts(IoReqList,
|
|
IrqsToDeleteCount,
|
|
socket->IrqMask,
|
|
&newReqList,
|
|
IsSocketFlagSet(socket, SOCKET_CB_ROUTE_R2_TO_PCI)
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
Irp->IoStatus.Information = (ULONG_PTR) newReqList;
|
|
ExFreePool(IoReqList);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PcmciaQueryDeviceText(
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN OUT PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns descriptive text information about the
|
|
PDO (location and device desc.)
|
|
|
|
Arguments:
|
|
|
|
Pdo - Pointer to the PC-Card's device object
|
|
Irp - IRP_MN_QUERY_DEVICE_TEXT Irp
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
STATUS_NOT_SUPPORTED - if not supported
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension;
|
|
UNICODE_STRING unicodeString;
|
|
ANSI_STRING ansiString;
|
|
UCHAR deviceText[128];
|
|
NTSTATUS status;
|
|
USHORT deviceTextLength;
|
|
PSOCKET_DATA socketData = pdoExtension->SocketData;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (irpStack->Parameters.QueryDeviceText.DeviceTextType == DeviceTextDescription) {
|
|
|
|
if (*(socketData->Mfg) == '\0' ) {
|
|
if (socketData->Flags & SDF_JEDEC_ID) {
|
|
sprintf(deviceText, "%s %s-%04x", PCMCIA_ID_STRING, PCMCIA_MEMORY_ID_STRING, socketData->JedecId);
|
|
|
|
} else {
|
|
sprintf(deviceText, "%s %s", PCMCIA_ID_STRING, PCMCIA_UNKNOWN_MANUFACTURER_STRING);
|
|
}
|
|
} else {
|
|
sprintf(deviceText, "%s %s", socketData->Mfg, socketData->Ident);
|
|
}
|
|
RtlInitAnsiString(&ansiString, deviceText);
|
|
|
|
deviceTextLength = (strlen(deviceText) + 1)*sizeof(WCHAR);
|
|
unicodeString.Buffer = ExAllocatePool(PagedPool, deviceTextLength);
|
|
if (unicodeString.Buffer == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
unicodeString.MaximumLength = deviceTextLength;
|
|
unicodeString.Length = 0;
|
|
|
|
status = RtlAnsiStringToUnicodeString(&unicodeString, &ansiString, FALSE);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(unicodeString.Buffer);
|
|
return status;
|
|
}
|
|
|
|
unicodeString.Buffer[unicodeString.Length/sizeof(WCHAR)] = L'\0';
|
|
Irp->IoStatus.Information = (ULONG_PTR) unicodeString.Buffer;
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
status = STATUS_NOT_SUPPORTED ;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PcmciaGetPcCardResourceRequirements(
|
|
PPDO_EXTENSION pdoExtension,
|
|
PULONG_PTR Information
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Fills in the resource requirements for the PC-Card obtained from the tuple information
|
|
|
|
Arguments:
|
|
|
|
PdoExtension - Pointer to the device extension for the PDO of the pc-card
|
|
Information - Pointer to an allocated resource requirements list is stored in this
|
|
argument. Caller's responsibility to free the list
|
|
|
|
Return value:
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES Could not allocate the list
|
|
STATUS_SUCCES Obtained resource requirements, Information contains the pointer
|
|
to the IO_RESOURCE_REQUIREMENTS list
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PPCMCIA_RESOURCE_CHAIN resListChain = NULL;
|
|
PIO_RESOURCE_REQUIREMENTS_LIST ioResourceRequirementsList;
|
|
PSOCKET socket = pdoExtension->Socket;
|
|
PSOCKET_DATA socketData = pdoExtension->SocketData;
|
|
PCONFIG_ENTRY currentConfigEntry;
|
|
PFDO_EXTENSION fdoExtension = socket->DeviceExtension;
|
|
CONFIG_LIST configList;
|
|
|
|
PAGED_CODE();
|
|
ASSERT (!IsDeviceMultifunction(pdoExtension));
|
|
|
|
//
|
|
// Run through the config entry chains for IO space & Mem space requirements
|
|
//
|
|
configList.SocketData = socketData;
|
|
|
|
for (currentConfigEntry=socketData->ConfigEntryChain; currentConfigEntry != NULL; currentConfigEntry=currentConfigEntry->NextEntry) {
|
|
|
|
if (currentConfigEntry->Flags & PCMCIA_INVALID_CONFIGURATION) {
|
|
continue;
|
|
}
|
|
|
|
configList.ConfigEntry = currentConfigEntry;
|
|
|
|
status = PcmciaConfigEntriesToResourceListChain(pdoExtension,
|
|
&configList,
|
|
(ULONG)1,
|
|
&resListChain
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
status = PcmciaMergeResourceChainToList(resListChain, &ioResourceRequirementsList);
|
|
|
|
if (NT_SUCCESS(status) && (ioResourceRequirementsList != NULL)) {
|
|
ioResourceRequirementsList->InterfaceType = Isa;
|
|
ioResourceRequirementsList->BusNumber = fdoExtension->Configuration.BusNumber;
|
|
ioResourceRequirementsList->SlotNumber = 0; // Need to revisit this..
|
|
*Information = (ULONG_PTR) ioResourceRequirementsList;
|
|
}
|
|
|
|
PcmciaFreeResourceChain(resListChain);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PcmciaConfigEntriesToResourceListChain(
|
|
PPDO_EXTENSION pdoExtension,
|
|
PCONFIG_LIST ConfigList,
|
|
ULONG configCount,
|
|
PPCMCIA_RESOURCE_CHAIN *ResListChainHead
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Fills in the resource requirements for the PC-Card obtained from the tuple information
|
|
|
|
Arguments:
|
|
|
|
PdoExtension - Pointer to the device extension for the PDO of the pc-card
|
|
|
|
Return value:
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES Could not allocate the list
|
|
STATUS_SUCCES Obtained resource requirements, Information contains the pointer
|
|
to the IO_RESOURCE_REQUIREMENTS list
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
PSOCKET socket = pdoExtension->Socket;
|
|
PFDO_EXTENSION fdoExtension = socket->DeviceExtension;
|
|
PCONFIG_ENTRY currentConfigEntry;
|
|
PPCMCIA_RESOURCE_CHAIN resListChain;
|
|
PIO_RESOURCE_LIST ioResourceList;
|
|
PIO_RESOURCE_DESCRIPTOR ioResourceDesc;
|
|
ULONG irqMask = 0, i, iConfig;
|
|
ULONG totalDescriptorCount = 0;
|
|
ULONG TotalIoRanges = 0;
|
|
ULONG listSize;
|
|
BOOLEAN IoRangeIs16Bit[MAX_NUMBER_OF_IO_RANGES] = {0};
|
|
BOOLEAN irqAlternative;
|
|
|
|
PAGED_CODE();
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "pdo %08x build ioreslist from configlist %08x, config count=%d\n",
|
|
pdoExtension->DeviceObject, ConfigList, configCount));
|
|
|
|
//
|
|
// Calculate how many descriptors we need. This involves also generating
|
|
// the irqmask with the intersection of the irqmasks of the config entries
|
|
// in the array.
|
|
//
|
|
|
|
for (iConfig = 0; iConfig < configCount; iConfig++) {
|
|
currentConfigEntry = ConfigList[iConfig].ConfigEntry;
|
|
|
|
irqMask |= currentConfigEntry->IrqMask;
|
|
|
|
totalDescriptorCount += currentConfigEntry->NumberOfIoPortRanges;
|
|
totalDescriptorCount += currentConfigEntry->NumberOfMemoryRanges;
|
|
}
|
|
|
|
if (irqMask) {
|
|
if (IsSocketFlagSet(socket, SOCKET_CB_ROUTE_R2_TO_PCI)) {
|
|
totalDescriptorCount++;
|
|
} else {
|
|
totalDescriptorCount += PcmciaCountOnes(socket->IrqMask);
|
|
}
|
|
}
|
|
|
|
if (!totalDescriptorCount) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Add one more for our private DPTYPE_PCMCIA_CONFIGURATION
|
|
//
|
|
totalDescriptorCount++;
|
|
|
|
if (configCount > 1) {
|
|
//
|
|
// Add more for our private DPTYPE_PCMCIA_MF_CONFIGURATION
|
|
//
|
|
totalDescriptorCount+=configCount;
|
|
}
|
|
|
|
//
|
|
// Calculate the size of IO_RESOURCE_LIST, allocate and clear it
|
|
//
|
|
|
|
listSize = (totalDescriptorCount - 1) * sizeof(IO_RESOURCE_DESCRIPTOR)
|
|
+ sizeof(IO_RESOURCE_LIST);
|
|
|
|
ioResourceList = ExAllocatePool(PagedPool, listSize);
|
|
if (ioResourceList == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(ioResourceList, listSize);
|
|
|
|
//
|
|
// Now that we have the resource list allocated, fill it in
|
|
//
|
|
|
|
ioResourceList->Version = IO_RESOURCE_LIST_VERSION;
|
|
ioResourceList->Revision = IO_RESOURCE_LIST_REVISION;
|
|
ioResourceList->Count = totalDescriptorCount;
|
|
ioResourceDesc = &ioResourceList->Descriptors[0];
|
|
|
|
//
|
|
// Fill in the IRQ info.
|
|
//
|
|
if (irqMask) {
|
|
if (IsSocketFlagSet(socket, SOCKET_CB_ROUTE_R2_TO_PCI)) {
|
|
|
|
ioResourceDesc->Type = CmResourceTypeInterrupt;
|
|
ioResourceDesc->Option = 0;
|
|
|
|
ioResourceDesc->Flags = (USHORT)CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
|
|
ioResourceDesc->ShareDisposition = CmResourceShareShared;
|
|
ioResourceDesc->u.Interrupt.MinimumVector = socket->FdoIrq;
|
|
ioResourceDesc->u.Interrupt.MaximumVector = socket->FdoIrq;
|
|
ioResourceDesc++;
|
|
|
|
} else {
|
|
ULONG irqn;
|
|
|
|
irqMask = socket->IrqMask;
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "irq mask %04x\n", irqMask));
|
|
|
|
//
|
|
// For each IRQ supported, fill in a separate IO descriptor
|
|
//
|
|
irqAlternative = FALSE;
|
|
for (irqn = 0 ;irqMask; irqMask = (irqMask >> 1), irqn++) {
|
|
if (irqMask & 0x1) {
|
|
if (irqAlternative) {
|
|
ioResourceDesc->Option = IO_RESOURCE_ALTERNATIVE;
|
|
} else {
|
|
irqAlternative = TRUE;
|
|
ioResourceDesc->Option = 0;
|
|
}
|
|
ioResourceDesc->Type = CmResourceTypeInterrupt;
|
|
//
|
|
// This is for 16-bit pc-cards.. so request an edge-triggered
|
|
// exclusive interrupt
|
|
//
|
|
ioResourceDesc->Flags = (USHORT)CM_RESOURCE_INTERRUPT_LATCHED;
|
|
ioResourceDesc->ShareDisposition = CmResourceShareDeviceExclusive;
|
|
ioResourceDesc->u.Interrupt.MinimumVector =
|
|
ioResourceDesc->u.Interrupt.MaximumVector = irqn;
|
|
ioResourceDesc++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
for (iConfig = 0; iConfig < configCount; iConfig++) {
|
|
currentConfigEntry = ConfigList[iConfig].ConfigEntry;
|
|
|
|
//
|
|
// for each I/O range, fill in an IoResourceDescriptor
|
|
//
|
|
for (i = 0; i < currentConfigEntry->NumberOfIoPortRanges; i++) {
|
|
PHYSICAL_ADDRESS port;
|
|
|
|
ioResourceDesc->Option = 0;
|
|
ioResourceDesc->Type = CmResourceTypePort;
|
|
ioResourceDesc->Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_16_BIT_DECODE;
|
|
ioResourceDesc->ShareDisposition = CmResourceShareDeviceExclusive;
|
|
|
|
if (currentConfigEntry->IoPortBase[i] == 0) {
|
|
//
|
|
// This is a flexible requirement. Basically means we need
|
|
// any system address range of specified length & alignment
|
|
//
|
|
port=RtlConvertUlongToLargeInteger(fdoExtension->IoLow);
|
|
ioResourceDesc->u.Port.MinimumAddress = port;
|
|
port=RtlConvertUlongToLargeInteger(fdoExtension->IoHigh);
|
|
ioResourceDesc->u.Port.MaximumAddress = port;
|
|
} else {
|
|
|
|
port = RtlConvertUlongToLargeInteger((ULONG) currentConfigEntry->IoPortBase[i]);
|
|
ioResourceDesc->u.Port.MinimumAddress = port;
|
|
port = RtlConvertUlongToLargeInteger((ULONG) (currentConfigEntry->IoPortBase[i]+
|
|
currentConfigEntry->IoPortLength[i]));
|
|
ioResourceDesc->u.Port.MaximumAddress = port;
|
|
}
|
|
ioResourceDesc->u.Port.Length = (ULONG) currentConfigEntry->IoPortLength[i]+1;
|
|
ioResourceDesc->u.Port.Alignment = currentConfigEntry->IoPortAlignment[i];
|
|
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "Port range: %08x-%08x, Length %04x\n",
|
|
ioResourceDesc->u.Port.MinimumAddress.LowPart,
|
|
ioResourceDesc->u.Port.MaximumAddress.LowPart,
|
|
ioResourceDesc->u.Port.Length
|
|
));
|
|
|
|
if ((TotalIoRanges < MAX_NUMBER_OF_IO_RANGES) &&
|
|
(currentConfigEntry->Io16BitAccess)) {
|
|
IoRangeIs16Bit[TotalIoRanges] = TRUE;
|
|
}
|
|
TotalIoRanges++;
|
|
|
|
ioResourceDesc++;
|
|
}
|
|
}
|
|
|
|
for (iConfig = 0; iConfig < configCount; iConfig++) {
|
|
currentConfigEntry = ConfigList[iConfig].ConfigEntry;
|
|
|
|
//
|
|
// for each memory range, fill in an IoResourceDescriptor
|
|
//
|
|
for (i = 0; i < currentConfigEntry->NumberOfMemoryRanges; i++) {
|
|
PHYSICAL_ADDRESS mem;
|
|
|
|
ioResourceDesc->Option = 0;
|
|
ioResourceDesc->Type = CmResourceTypeMemory;
|
|
ioResourceDesc->Flags = CM_RESOURCE_MEMORY_READ_WRITE;
|
|
ioResourceDesc->ShareDisposition = CmResourceShareDeviceExclusive;
|
|
if (currentConfigEntry->MemoryHostBase[i]) {
|
|
mem = RtlConvertUlongToLargeInteger((ULONG) currentConfigEntry->MemoryHostBase[i]);
|
|
ioResourceDesc->u.Memory.MinimumAddress = mem;
|
|
mem = RtlConvertUlongToLargeInteger((ULONG) currentConfigEntry->MemoryHostBase[i]+
|
|
(ULONG) currentConfigEntry->MemoryLength[i]-1);
|
|
ioResourceDesc->u.Memory.MaximumAddress = mem;
|
|
} else {
|
|
//
|
|
// Any physical address is ok
|
|
//
|
|
mem = RtlConvertUlongToLargeInteger(0);
|
|
ioResourceDesc->u.Memory.MinimumAddress = mem;
|
|
//
|
|
// Only decode 24 bit memory addresses if there is no page register
|
|
//
|
|
if ((fdoExtension->Flags & PCMCIA_MEMORY_24BIT) == 0) {
|
|
mem = RtlConvertUlongToLargeInteger(0xFFFFFFFF);
|
|
} else {
|
|
mem = RtlConvertUlongToLargeInteger(0xFFFFFF);
|
|
}
|
|
ioResourceDesc->u.Memory.MaximumAddress = mem;
|
|
}
|
|
|
|
ioResourceDesc->u.Memory.Length = currentConfigEntry->MemoryLength[i];
|
|
//
|
|
// Alignment has to be 12 bits
|
|
//
|
|
ioResourceDesc->u.Memory.Alignment = 0x1000;
|
|
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "Mem range: %08x-%08x, Length %08x\n",
|
|
ioResourceDesc->u.Memory.MinimumAddress.LowPart,
|
|
ioResourceDesc->u.Memory.MaximumAddress.LowPart,
|
|
ioResourceDesc->u.Memory.Length
|
|
));
|
|
|
|
ioResourceDesc++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Fill in device private containing our config index
|
|
//
|
|
ioResourceDesc->Option = 0;
|
|
ioResourceDesc->Type = CmResourceTypeDevicePrivate;
|
|
PCMRES_SET_DESCRIPTOR_TYPE(ioResourceDesc, DPTYPE_PCMCIA_CONFIGURATION);
|
|
|
|
currentConfigEntry = ConfigList[0].ConfigEntry;
|
|
PCMRES_SET_CONFIG_INDEX(ioResourceDesc, currentConfigEntry->IndexForThisConfiguration);
|
|
|
|
for (i = 0; i < MAX_NUMBER_OF_IO_RANGES; i++) {
|
|
if (IoRangeIs16Bit[i]) {
|
|
PCMRES_SET_IO_FLAG(ioResourceDesc, i, PCMRESF_IO_16BIT_ACCESS);
|
|
PCMRES_SET_IO_FLAG(ioResourceDesc, i, PCMRESF_IO_SOURCE_16);
|
|
PCMRES_SET_IO_FLAG(ioResourceDesc, i, PCMRESF_IO_WAIT_16);
|
|
}
|
|
}
|
|
|
|
PCMRES_SET_MEMORY_CARDBASE(ioResourceDesc, 0, currentConfigEntry->MemoryCardBase[0]);
|
|
PCMRES_SET_MEMORY_CARDBASE(ioResourceDesc, 1, currentConfigEntry->MemoryCardBase[1]);
|
|
|
|
//
|
|
// Set defaults
|
|
//
|
|
PCMRES_SET_MEMORY_WAITSTATES(ioResourceDesc, 0, PCMRESF_MEM_WAIT_3);
|
|
PCMRES_SET_MEMORY_WAITSTATES(ioResourceDesc, 1, PCMRESF_MEM_WAIT_3);
|
|
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "device private %08x %08x %08x\n",
|
|
ioResourceDesc->u.DevicePrivate.Data[0],
|
|
ioResourceDesc->u.DevicePrivate.Data[1],
|
|
ioResourceDesc->u.DevicePrivate.Data[2]
|
|
));
|
|
|
|
ioResourceDesc++;
|
|
|
|
//
|
|
// Fill in device private for MF configurations
|
|
//
|
|
|
|
if (configCount > 1) {
|
|
|
|
for (iConfig = 0; iConfig < configCount; iConfig++) {
|
|
PSOCKET_DATA socketData;
|
|
|
|
currentConfigEntry = ConfigList[iConfig].ConfigEntry;
|
|
socketData = ConfigList[iConfig].SocketData;
|
|
|
|
ioResourceDesc->Option = 0;
|
|
ioResourceDesc->Type = CmResourceTypeDevicePrivate;
|
|
PCMRES_SET_DESCRIPTOR_TYPE(ioResourceDesc, DPTYPE_PCMCIA_MF_CONFIGURATION);
|
|
|
|
PCMRES_SET_CONFIG_OPTIONS(ioResourceDesc, currentConfigEntry->IndexForThisConfiguration);
|
|
PCMRES_SET_PORT_RESOURCE_INDEX(ioResourceDesc, socketData->MfIoPortResourceMapIndex);
|
|
if (socketData->DeviceType == PCCARD_TYPE_MODEM) {
|
|
PCMRES_SET_AUDIO_ENABLE(ioResourceDesc);
|
|
}
|
|
PCMRES_SET_CONFIG_REGISTER_BASE(ioResourceDesc, socketData->ConfigRegisterBase);
|
|
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "device private MF %08x %08x %08x\n",
|
|
ioResourceDesc->u.DevicePrivate.Data[0],
|
|
ioResourceDesc->u.DevicePrivate.Data[1],
|
|
ioResourceDesc->u.DevicePrivate.Data[2]
|
|
));
|
|
ioResourceDesc++;
|
|
}
|
|
}
|
|
|
|
ASSERT(ioResourceDesc == &ioResourceList->Descriptors[ioResourceList->Count]);
|
|
|
|
//
|
|
// Allocate an PCMCIA_RESOURCE_CHAIN structure to track the IO_RESOURCE_LIST.
|
|
//
|
|
resListChain = ExAllocatePool(PagedPool, sizeof(PCMCIA_RESOURCE_CHAIN));
|
|
if (resListChain == NULL) {
|
|
ExFreePool(ioResourceList);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
resListChain->IoResList = ioResourceList;
|
|
|
|
//
|
|
// Link this new node onto the passed in chain
|
|
//
|
|
resListChain->NextList = *ResListChainHead;
|
|
*ResListChainHead = resListChain;
|
|
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "Added resource chain node %08x, ioreslist %08x\n",
|
|
resListChain, ioResourceList));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PcmciaMergeResourceChainToList(
|
|
PPCMCIA_RESOURCE_CHAIN ResListChainHead,
|
|
PIO_RESOURCE_REQUIREMENTS_LIST *GeneratedResourceRequirementsList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PPCMCIA_RESOURCE_CHAIN resListChain;
|
|
PIO_RESOURCE_REQUIREMENTS_LIST ioResourceRequirementsList;
|
|
PIO_RESOURCE_LIST newIoResList, oldIoResList;
|
|
ULONG listSize, listCount, totalDescriptorCount;
|
|
|
|
PAGED_CODE();
|
|
//
|
|
// Now merge the newly created IO_RESOURCE_LISTs into one big
|
|
// IO_RESOURCE_REQUIREMENTS list.
|
|
//
|
|
|
|
listCount = 0;
|
|
totalDescriptorCount = 0;
|
|
|
|
for (resListChain = ResListChainHead; resListChain != NULL; resListChain = resListChain->NextList) {
|
|
listCount++;
|
|
totalDescriptorCount += resListChain->IoResList->Count;
|
|
}
|
|
|
|
if (totalDescriptorCount > 0) {
|
|
|
|
listSize = (totalDescriptorCount - listCount) * sizeof(IO_RESOURCE_DESCRIPTOR)
|
|
+ (listCount-1) * sizeof(IO_RESOURCE_LIST)
|
|
+ sizeof(IO_RESOURCE_REQUIREMENTS_LIST);
|
|
|
|
//
|
|
// Allocate space for the res. req. list here
|
|
//
|
|
ioResourceRequirementsList = (PIO_RESOURCE_REQUIREMENTS_LIST) ExAllocatePool(PagedPool, listSize);
|
|
|
|
if (ioResourceRequirementsList == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Init the list
|
|
//
|
|
|
|
RtlZeroMemory(ioResourceRequirementsList, listSize);
|
|
ioResourceRequirementsList->ListSize = listSize;
|
|
ioResourceRequirementsList->AlternativeLists = listCount;
|
|
|
|
//
|
|
// Copy in all the other lists. Point the target pointer (newIoResList)
|
|
// at the end of the list, and lay each new list preceding the old
|
|
// one. This is done because the list is LIFO.
|
|
//
|
|
|
|
newIoResList = (PIO_RESOURCE_LIST) (((PUCHAR) ioResourceRequirementsList) + listSize);
|
|
|
|
for (resListChain = ResListChainHead; resListChain != NULL; resListChain = resListChain->NextList) {
|
|
|
|
oldIoResList = resListChain->IoResList;
|
|
listSize = sizeof(IO_RESOURCE_LIST) + (oldIoResList->Count-1)*sizeof(IO_RESOURCE_DESCRIPTOR);
|
|
newIoResList = (PIO_RESOURCE_LIST) (((PUCHAR) newIoResList) - listSize);
|
|
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "Merge resource chain node %08x, ioreslist %08x\n",
|
|
resListChain, oldIoResList));
|
|
|
|
RtlCopyMemory(newIoResList, oldIoResList, listSize);
|
|
}
|
|
|
|
ASSERT(newIoResList == &ioResourceRequirementsList->List[0]);
|
|
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "Resource chain merged to ioResourceRequirementsList %08x\n",
|
|
ioResourceRequirementsList));
|
|
|
|
} else {
|
|
ioResourceRequirementsList = NULL;
|
|
}
|
|
|
|
*GeneratedResourceRequirementsList = ioResourceRequirementsList;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
PcmciaFreeResourceChain(
|
|
PPCMCIA_RESOURCE_CHAIN ResListChain
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PPCMCIA_RESOURCE_CHAIN prevResListChain;
|
|
|
|
PAGED_CODE();
|
|
//
|
|
// free the temporary structures
|
|
//
|
|
while (ResListChain != NULL) {
|
|
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "Delete resource chain node %08x, ioreslist %08x\n",
|
|
ResListChain, ResListChain->IoResList));
|
|
if (ResListChain->IoResList) {
|
|
ExFreePool(ResListChain->IoResList);
|
|
}
|
|
prevResListChain = ResListChain;
|
|
ResListChain = ResListChain->NextList;
|
|
ExFreePool(prevResListChain);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PcmciaMfEnumerateConfigurations(
|
|
IN PPDO_EXTENSION PdoExtension,
|
|
IN PSOCKET_DATA socketData,
|
|
PCONFIG_LIST ConfigList,
|
|
IN ULONG Depth,
|
|
PPCMCIA_RESOURCE_CHAIN *MfResListChain
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is responsible for enumerating combinations of resource
|
|
requirements for the functions in a true R2 MF card. It is called
|
|
recursively to perform this function.
|
|
|
|
See the description of PcmciaMfGetResourceRequirements() to see the
|
|
initial state. This routine runs down the linked list of SocketData
|
|
structures, and each corresponding linked list of ConfigData structures.
|
|
|
|
When it finds itself at the end of the SocketData list, it is ready
|
|
to build an IoResList for a single permutation. It calls
|
|
PcmciaConfigEntriesToResourceList() to build a single IoResList.
|
|
|
|
In the example MF card with two functions, and 2 and 3 resource
|
|
alternatives respectively, this routine will build a list that looks
|
|
something like this:
|
|
|
|
+--------------+
|
|
|MfResListChain|
|
|
+--------------+
|
|
| +-----------------------+ +----------------+
|
|
+--------|MF_RESOURCE_LIST(A1+B1)|-----|IoResList(A1+B1)|
|
|
+-----------------------+ +----------------+
|
|
|
|
|
+-----------------------+ +----------------+
|
|
|MF_RESOURCE_LIST(A1+B2)|-----|IoResList(A1+B2)|
|
|
+-----------------------+ +----------------+
|
|
|
|
|
+-----------------------+ +----------------+
|
|
|MF_RESOURCE_LIST(A1+B3)|-----|IoResList(A1+B3)|
|
|
+-----------------------+ +----------------+
|
|
|
|
|
+-----------------------+ +----------------+
|
|
|MF_RESOURCE_LIST(A2+B1)|-----|IoResList(A2+B1)|
|
|
+-----------------------+ +----------------+
|
|
|
|
|
+-----------------------+ +----------------+
|
|
|MF_RESOURCE_LIST(A2+B2)|-----|IoResList(A2+B2)|
|
|
+-----------------------+ +----------------+
|
|
|
|
|
+-----------------------+ +----------------+
|
|
|MF_RESOURCE_LIST(A2+B3)|-----|IoResList(A2+B3)|
|
|
+-----------------------+ +----------------+
|
|
|
|
It returns to PcmciaMfGetResourceRequirements() when the list is complete.
|
|
|
|
|
|
Arguments:
|
|
|
|
PdoExtension - Pointer to the device extension for the PDO of the pc-card
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PFDO_EXTENSION fdoExtension = PdoExtension->Socket->DeviceExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!socketData) {
|
|
//
|
|
// End of SocketData chain, now ready to generate IoResList for this
|
|
//
|
|
|
|
if (PcmciaMfCheckForOverlappingRanges(ConfigList, (LONG)Depth)) {
|
|
//
|
|
// This combination would have generated a bad IoResList
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Build an io resource list from the current config list
|
|
//
|
|
|
|
status = PcmciaConfigEntriesToResourceListChain(PdoExtension,
|
|
ConfigList,
|
|
Depth,
|
|
MfResListChain
|
|
);
|
|
|
|
} else {
|
|
PCONFIG_ENTRY configEntry;
|
|
//
|
|
// Not at the bottom of the tree. Recurse through each config entry
|
|
// in this socket data.
|
|
//
|
|
|
|
ConfigList[Depth].SocketData = socketData;
|
|
|
|
for (configEntry = socketData->ConfigEntryChain;
|
|
configEntry != NULL; configEntry = configEntry->NextEntry) {
|
|
|
|
if (configEntry->Flags & PCMCIA_INVALID_CONFIGURATION) {
|
|
continue;
|
|
}
|
|
|
|
ConfigList[Depth].ConfigEntry = configEntry;
|
|
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "pdo %08x mf enum %d sktdata %08x configEntry %08x\n",
|
|
PdoExtension->DeviceObject, Depth, socketData, configEntry));
|
|
|
|
status = PcmciaMfEnumerateConfigurations(PdoExtension,
|
|
socketData->Next,
|
|
ConfigList,
|
|
Depth+1,
|
|
MfResListChain);
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PcmciaMfGetResourceRequirements(
|
|
IN PPDO_EXTENSION PdoExtension,
|
|
PULONG_PTR Information
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For true Multifunction R2 cards, this routine generates a conglomerate
|
|
IoResourceList based on the permutations of configurations of the
|
|
functions.
|
|
|
|
Initially the tuple parsing code builds an internal representation of
|
|
the configuration requirements of the cards with SocketData and
|
|
ConfigData structures. Each SocketData structure represents an
|
|
individual function, and each ConfigData represents an alternative
|
|
resource requirement list for that function.
|
|
|
|
So for example, an MF card that has two functions may have an internal
|
|
layout like this:
|
|
|
|
+------------+
|
|
|PdoExtension|
|
|
+------------+
|
|
| +-----------+ +-----------+
|
|
+----------|SocketDataA|----------|SocketDataB|----0
|
|
+-----------+ +-----------+
|
|
| |
|
|
+------------+ +------------+
|
|
|ConfigDataA1| |ConfigDataB1|
|
|
+------------+ +------------+
|
|
| |
|
|
+------------+ +------------+
|
|
|ConfigDataA2| |ConfigDataB2|
|
|
+------------+ +------------+
|
|
| |
|
|
0 +------------+
|
|
|ConfigDataB3|
|
|
+------------+
|
|
|
|
|
0
|
|
|
|
This example shows that function A has two resource requirement alternatives,
|
|
and function B has three. What we do is make permutations of each alternative,
|
|
e.g.:
|
|
A1-B1, A1-B2, A1-B3, A2-B1, A2-B2, A2-B3
|
|
|
|
The permutations are built with recursive calls to PcmciaMfEnumerateConfigurations().
|
|
In this example, the result of this enumeration will be six independent
|
|
IoResLists. Each IoResList will be a merge of the particular permutation. When
|
|
PcmciaMfEnumerateConfigurations() returns to this routine, these IoResLists will
|
|
be chained together through the pointer MfResListChain. The rest of the routine
|
|
builds a single "six-member" IoResList from the chain.
|
|
|
|
Finally the support structure and lists for performing the operation are freed, and
|
|
the single conglomerate list is returned.
|
|
|
|
|
|
Arguments:
|
|
|
|
PdoExtension - Pointer to the device extension for the PDO of the pc-card
|
|
Information - Pointer to an allocated resource requirements list is stored in this
|
|
argument. Caller's responsibility to free the list
|
|
|
|
Return value:
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES Could not allocate the list
|
|
STATUS_SUCCES Obtained resource requirements, Information contains the pointer
|
|
to the IO_RESOURCE_REQUIREMENTS list
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PFDO_EXTENSION fdoExtension = PdoExtension->Socket->DeviceExtension;
|
|
ULONG MaxDepth = 0;
|
|
PSOCKET_DATA socketData;
|
|
PCONFIG_LIST ConfigList;
|
|
PPCMCIA_RESOURCE_CHAIN MfResListChain = NULL;
|
|
PIO_RESOURCE_REQUIREMENTS_LIST ioResourceRequirementsList;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT (IsDeviceMultifunction(PdoExtension));
|
|
|
|
//
|
|
// Find out how deep the enumeration will be.
|
|
// Should be the same as Socket->NumberOfFunctions, but just to be paranoid...
|
|
//
|
|
for (socketData = PdoExtension->SocketData; socketData != NULL; socketData = socketData->Next) {
|
|
MaxDepth++;
|
|
}
|
|
|
|
if (!MaxDepth) {
|
|
ASSERT (PdoExtension->SocketData);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
ConfigList = ExAllocatePool(PagedPool, MaxDepth*sizeof(CONFIG_LIST));
|
|
|
|
if (!ConfigList) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
PcmciaMfBuildResourceMapInfo(PdoExtension, ConfigList, MaxDepth);
|
|
|
|
|
|
status = PcmciaMfEnumerateConfigurations(PdoExtension,
|
|
PdoExtension->SocketData,
|
|
ConfigList,
|
|
0,
|
|
&MfResListChain);
|
|
|
|
ExFreePool(ConfigList);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Now merge everything that EnumerateConfigurations built into a single list
|
|
//
|
|
status = PcmciaMergeResourceChainToList(MfResListChain, &ioResourceRequirementsList);
|
|
}
|
|
|
|
if (NT_SUCCESS(status) && (ioResourceRequirementsList != NULL)) {
|
|
ioResourceRequirementsList->InterfaceType = Isa;
|
|
ioResourceRequirementsList->BusNumber = fdoExtension->Configuration.BusNumber;
|
|
ioResourceRequirementsList->SlotNumber = 0; // Need to revisit this..
|
|
*Information = (ULONG_PTR) ioResourceRequirementsList;
|
|
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "pdo %08x mf returning req list %08x, %d alternatives\n",
|
|
PdoExtension->DeviceObject, ioResourceRequirementsList,
|
|
ioResourceRequirementsList->AlternativeLists
|
|
));
|
|
}
|
|
|
|
PcmciaFreeResourceChain(MfResListChain);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
PcmciaMfBuildResourceMapInfo(
|
|
IN PPDO_EXTENSION PdoExtension,
|
|
PCONFIG_LIST ConfigList,
|
|
ULONG ConfigCount
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes variables in the SocketData structures to allow
|
|
PcmciaMfEnumerateChild() to correctly build ChildInfo ResourceMaps for MF.SYS.
|
|
|
|
It needs to calculate the base index for a particular resource of a particular
|
|
function. So for the example of a 2-function MF R2 card, the resulting CmResList
|
|
will be layed out positionally like:
|
|
|
|
CmResList
|
|
IRQ (Shared)
|
|
I/O (Function A)
|
|
I/O (Function B)
|
|
Mem (Function A)
|
|
Mem (Function B)
|
|
|
|
The reason for this is this is simply how PcmciaConfigEntriesToResourceList()
|
|
happens to lay out the requirements. So in order to generate a valid resource
|
|
map, this routine has to calculate, for example, the Memory Base for function B
|
|
by adding together:
|
|
1 (if there will be an IRQ in the cmreslist)
|
|
# of I/O port resources for A
|
|
# of I/O port resources for B
|
|
# of Mem resource for A
|
|
This sum will give you the position in the CmReslist for the first memory resource
|
|
that B would use.
|
|
|
|
These calculations are stored in the socket data structures for each corresponding
|
|
function so that PcmciaMfEnumerateChild() can simply fill in the maps for MF.
|
|
|
|
Arguments:
|
|
|
|
PdoExtension - Pointer to the device extension for the PDO of the pc-card
|
|
ConfigList - config list array holds a permutation of configdata's
|
|
ConfigCount - # of card functions
|
|
|
|
Return value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PSOCKET_DATA socketData;
|
|
PCONFIG_ENTRY configEntry;
|
|
ULONG index;
|
|
USHORT count;
|
|
UCHAR currentResourceMapIndex = 0;
|
|
BOOLEAN needsIRQ = FALSE;
|
|
|
|
for (index = 0, socketData = PdoExtension->SocketData; socketData != NULL; socketData = socketData->Next) {
|
|
|
|
//
|
|
// In the current implementation we assume that all the alternative lists in the
|
|
// io resource requirements for the multifunction parent pc-card have the same number
|
|
// and types of resource requirements. i.e. it's currently illegal to request
|
|
// one configuration in which only IRQ and I/O are requested, for example, and an
|
|
// alternative configuration in which MEMORY is also specified.
|
|
// This is because of the limitation in the MF enumerator - (which in turn relied
|
|
// on the Win 9x implementation).
|
|
// So we currently look at only the first valid configuration - that is representative
|
|
// of all the other configurations.
|
|
//
|
|
for (configEntry = socketData->ConfigEntryChain; (configEntry != NULL) &&
|
|
(configEntry->Flags & PCMCIA_INVALID_CONFIGURATION);
|
|
configEntry = configEntry->NextEntry);
|
|
|
|
if (configEntry == NULL) {
|
|
return;
|
|
}
|
|
|
|
ASSERT(index < ConfigCount);
|
|
|
|
ConfigList[index].SocketData = socketData;
|
|
ConfigList[index].ConfigEntry = configEntry;
|
|
index++;
|
|
}
|
|
|
|
//
|
|
// IRQ is unique because it is the only shared resource. So if the card needs an IRQ, then
|
|
// all devices point to the same resource
|
|
|
|
for (index = 0; index < ConfigCount; index++) {
|
|
if (ConfigList[index].ConfigEntry->IrqMask) {
|
|
//
|
|
// Index always maps to zero since PcmciaConfigEntriesToResourceList
|
|
// builds IRQs first.
|
|
//
|
|
ConfigList[index].SocketData->MfIrqResourceMapIndex = currentResourceMapIndex;
|
|
ConfigList[index].SocketData->MfNeedsIrq = TRUE;
|
|
needsIRQ = TRUE;
|
|
}
|
|
}
|
|
|
|
if (needsIRQ) {
|
|
currentResourceMapIndex++;
|
|
}
|
|
|
|
//
|
|
// fill in the bases of the I/O port ranges
|
|
//
|
|
for (index = 0; index < ConfigCount; index++) {
|
|
ConfigList[index].SocketData->MfIoPortResourceMapIndex = currentResourceMapIndex;
|
|
|
|
count = ConfigList[index].ConfigEntry->NumberOfIoPortRanges;
|
|
|
|
ConfigList[index].SocketData->MfIoPortCount = count;
|
|
currentResourceMapIndex += count;
|
|
}
|
|
|
|
//
|
|
// fill in the bases of the memory ranges
|
|
//
|
|
for (index = 0; index < ConfigCount; index++) {
|
|
ConfigList[index].SocketData->MfMemoryResourceMapIndex = currentResourceMapIndex;
|
|
|
|
count = ConfigList[index].ConfigEntry->NumberOfMemoryRanges;
|
|
|
|
ConfigList[index].SocketData->MfMemoryCount = count;
|
|
currentResourceMapIndex += count;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
PcmciaMfCheckForOverlappingRanges(
|
|
PCONFIG_LIST ConfigList,
|
|
LONG ConfigCount
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine scans through the current config list to see if the set of configurations
|
|
overlap. For example, if the MF R2 card is a dual-serial card, then each serial device
|
|
may want to have one of the standard com addresses (e.g. 3f8, 2f8, etc.). But now
|
|
that we are merging configurations, we need to weed out any overlapping ranges so
|
|
we don't produce a congolmerate IoResList that contains the same range for different
|
|
functions.
|
|
|
|
Arguments:
|
|
|
|
ConfigList - config list array holds a permutation of configdata's
|
|
ConfigCount - # of card functions
|
|
|
|
Return value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PCONFIG_ENTRY configEntry1, configEntry2;
|
|
LONG configIndex1, configIndex2;
|
|
LONG configCount1, configCount2;
|
|
LONG rangeIndex1, rangeIndex2;
|
|
LONG rangeCount1, rangeCount2;
|
|
ULONG rangeStart1, rangeStart2;
|
|
ULONG rangeEnd1, rangeEnd2;
|
|
BOOLEAN rangesOverlap = FALSE;
|
|
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "-------Range Check--------------\n"));
|
|
//
|
|
// Check for overlap in the I/O port ranges
|
|
//
|
|
try {
|
|
for (configIndex1 = 0; configIndex1 < ConfigCount; configIndex1++) {
|
|
|
|
configEntry1 = ConfigList[configIndex1].ConfigEntry;
|
|
rangeCount1 = configEntry1->NumberOfIoPortRanges;
|
|
|
|
for (rangeIndex1 = 0; rangeIndex1 < rangeCount1; rangeIndex1++) {
|
|
//
|
|
// Get the current range we will compare
|
|
//
|
|
rangeStart1 = configEntry1->IoPortBase[rangeIndex1];
|
|
rangeEnd1 = rangeStart1 + configEntry1->IoPortLength[rangeIndex1];
|
|
|
|
if (rangeStart1 == 0) {
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "RangeCheck I/O: skip unrestricted range %x.%x\n",
|
|
configIndex1, rangeIndex1));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now start comparing this against the rest of the ranges by
|
|
// starting at then end and working backwards.
|
|
//
|
|
for (configIndex2 = ConfigCount-1; configIndex2 >= 0; configIndex2--) {
|
|
configEntry2 = ConfigList[configIndex2].ConfigEntry;
|
|
rangeCount2 = configEntry2->NumberOfIoPortRanges;
|
|
|
|
for (rangeIndex2 = rangeCount2-1; rangeIndex2 >= 0; rangeIndex2--) {
|
|
|
|
if ((configEntry1 == configEntry2) && (rangeIndex1 == rangeIndex2)) {
|
|
leave;
|
|
}
|
|
|
|
rangeStart2 = configEntry2->IoPortBase[rangeIndex2];
|
|
rangeEnd2 = rangeStart2 + configEntry2->IoPortLength[rangeIndex2];
|
|
|
|
if (rangeStart2 == 0) {
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "RangeCheck I/O: skip unrestricted range %x.%x\n",
|
|
configIndex2, rangeIndex2));
|
|
continue;
|
|
}
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "RangeCheck I/O: %x.%x %04x-%04x :: %x.%x %04x-%04x\n",
|
|
configIndex1, rangeIndex1, rangeStart1, rangeEnd1,
|
|
configIndex2, rangeIndex2, rangeStart2, rangeEnd2));
|
|
|
|
if (((rangeStart1 >= rangeStart2) && (rangeStart1 <= rangeEnd2)) ||
|
|
((rangeEnd1 >= rangeStart2) && (rangeEnd1 <= rangeEnd2))) {
|
|
rangesOverlap = TRUE;
|
|
leave;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
}
|
|
|
|
if (rangesOverlap) {
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "-------Overlap Detected---------\n"));
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Check for overlap in the memory ranges
|
|
//
|
|
try {
|
|
for (configIndex1 = 0; configIndex1 < ConfigCount; configIndex1++) {
|
|
|
|
configEntry1 = ConfigList[configIndex1].ConfigEntry;
|
|
rangeCount1 = configEntry1->NumberOfMemoryRanges;
|
|
|
|
for (rangeIndex1 = 0; rangeIndex1 < rangeCount1; rangeIndex1++) {
|
|
//
|
|
// Get the current range we will compare
|
|
//
|
|
rangeStart1 = configEntry1->MemoryHostBase[rangeIndex1];
|
|
rangeEnd1 = rangeStart1 + configEntry1->MemoryLength[rangeIndex1] - 1;
|
|
|
|
if (rangeStart1 == 0) {
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "RangeCheck MEM: skip unrestricted range %x.%x\n",
|
|
configIndex1, rangeIndex1));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now start comparing this against the rest of the ranges by
|
|
// starting at then end and working backwards.
|
|
//
|
|
for (configIndex2 = ConfigCount-1; configIndex2 >= 0; configIndex2--) {
|
|
configEntry2 = ConfigList[configIndex2].ConfigEntry;
|
|
rangeCount2 = configEntry2->NumberOfMemoryRanges;
|
|
|
|
for (rangeIndex2 = rangeCount2-1; rangeIndex2 >= 0; rangeIndex2--) {
|
|
|
|
if ((configEntry1 == configEntry2) && (rangeIndex1 == rangeIndex2)) {
|
|
leave;
|
|
}
|
|
|
|
rangeStart2 = configEntry2->MemoryHostBase[rangeIndex2];
|
|
rangeEnd2 = rangeStart2 + configEntry2->MemoryLength[rangeIndex2] - 1;
|
|
|
|
if (rangeStart2 == 0) {
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "RangeCheck MEM: skip unrestricted range %x.%x\n",
|
|
configIndex2, rangeIndex2));
|
|
continue;
|
|
}
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "RangeCheck MEM: %x.%x %08x-%08x :: %x.%x %08x-%08x\n",
|
|
configIndex1, rangeIndex1, rangeStart1, rangeEnd1,
|
|
configIndex2, rangeIndex2, rangeStart2, rangeEnd2));
|
|
|
|
if (((rangeStart1 >= rangeStart2) && (rangeStart1 <= rangeEnd2)) ||
|
|
((rangeEnd1 >= rangeStart2) && (rangeEnd1 <= rangeEnd2))) {
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "-------Overlap Detected---------\n"));
|
|
rangesOverlap = TRUE;
|
|
leave;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
}
|
|
|
|
#if DBG
|
|
if (rangesOverlap) {
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "-------Overlap Detected---------\n"));
|
|
} else {
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "-------Generate IoResList-------\n"));
|
|
}
|
|
#endif
|
|
|
|
return rangesOverlap;
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PcmciaStartPcCard(
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN PCM_RESOURCE_LIST ResourceList,
|
|
IN OUT PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to start the PC-Card by configuring it with the supplied resources.
|
|
|
|
|
|
Arguments:
|
|
|
|
Pdo - Pointer to the device object representing the PC-Card which needs to be started
|
|
ResourceList - Pointer the list of assigned resources for the PC-Card
|
|
|
|
Return value:
|
|
|
|
STATUS_INSUFFICIENT_RESOURCES - Not sufficient resources supplied to start device/
|
|
could not allocate memory
|
|
STATUS_UNSUCCESSFUL - Supplied resources are invalid for this PC-Card
|
|
STATUS_SUCCESS - Configured and started the card successfully
|
|
|
|
--*/
|
|
{
|
|
PCM_FULL_RESOURCE_DESCRIPTOR fullResourceDesc;
|
|
PCM_PARTIAL_RESOURCE_LIST partialResourceList;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR partialResourceDesc;
|
|
ULONG fullResourceDescCount, partialResourceDescCount, i, index;
|
|
PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension;
|
|
PSOCKET socket = pdoExtension->Socket;
|
|
PFDO_EXTENSION fdoExtension = socket->DeviceExtension;
|
|
PSOCKET_DATA socketData = pdoExtension->SocketData;
|
|
PSOCKET_CONFIGURATION socketConfig;
|
|
PCONFIG_ENTRY currentConfig;
|
|
PFUNCTION_CONFIGURATION fnConfig;
|
|
NTSTATUS status;
|
|
ULONG scIoIndex = 0, scMemIndex = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (IsDeviceStarted(pdoExtension)) {
|
|
//
|
|
// Already started..
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (IsDevicePhysicallyRemoved(pdoExtension)) {
|
|
return STATUS_DEVICE_DOES_NOT_EXIST;
|
|
}
|
|
|
|
if ( ResourceList == NULL ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
PcmciaCleanupSocketConfiguration(pdoExtension);
|
|
|
|
socketConfig = ExAllocatePool(NonPagedPool, sizeof(SOCKET_CONFIGURATION));
|
|
if (!socketConfig) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(socketConfig, sizeof(SOCKET_CONFIGURATION));
|
|
|
|
fullResourceDescCount = ResourceList->Count;
|
|
|
|
ASSERT(fullResourceDescCount == 1);
|
|
|
|
fullResourceDesc = &ResourceList->List[0];
|
|
partialResourceList = &fullResourceDesc->PartialResourceList;
|
|
partialResourceDesc = partialResourceList->PartialDescriptors;
|
|
partialResourceDescCount = partialResourceList->Count;
|
|
|
|
socketConfig->NumberOfIoPortRanges =
|
|
socketConfig->NumberOfMemoryRanges = 0;
|
|
|
|
for (i=0; i< partialResourceList->Count; i++, partialResourceDesc++) {
|
|
switch (partialResourceDesc->Type) {
|
|
case CmResourceTypePort: {
|
|
index = socketConfig->NumberOfIoPortRanges;
|
|
socketConfig->Io[index].Base = partialResourceDesc->u.Port.Start.LowPart;
|
|
socketConfig->Io[index].Length = (USHORT) partialResourceDesc->u.Port.Length-1;
|
|
socketConfig->NumberOfIoPortRanges++;
|
|
break;
|
|
}
|
|
|
|
case CmResourceTypeMemory: {
|
|
index = socketConfig->NumberOfMemoryRanges;
|
|
socketConfig->Memory[index].HostBase = partialResourceDesc->u.Memory.Start.LowPart;
|
|
socketConfig->Memory[index].Length = partialResourceDesc->u.Memory.Length;
|
|
socketConfig->NumberOfMemoryRanges++;
|
|
break;
|
|
}
|
|
|
|
case CmResourceTypeInterrupt: {
|
|
socketConfig->Irq = partialResourceDesc->u.Interrupt.Level;
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "Assigned Irq: 0x%x for socket register offset %d\n",
|
|
socketConfig->Irq, socket->RegisterOffset));
|
|
break;
|
|
}
|
|
|
|
case CmResourceTypePcCardConfig:
|
|
case CmResourceTypeMfCardConfig:
|
|
case CmResourceTypeDevicePrivate: {
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "DevicePrivate received, Data= %08x %08x %08x\n",
|
|
partialResourceDesc->u.DevicePrivate.Data[0],
|
|
partialResourceDesc->u.DevicePrivate.Data[1],
|
|
partialResourceDesc->u.DevicePrivate.Data[2]));
|
|
|
|
if (PCMRES_GET_DESCRIPTOR_TYPE (partialResourceDesc) == DPTYPE_PCMCIA_CONFIGURATION) {
|
|
//
|
|
// Single function configuration private
|
|
//
|
|
socketConfig->IndexForCurrentConfiguration = PCMRES_GET_CONFIG_INDEX(partialResourceDesc);
|
|
socketConfig->ConfigRegisterBase = socketData->ConfigRegisterBase;
|
|
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "Pccard config resource\n"));
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, " Index %x\n", socketConfig->IndexForCurrentConfiguration));
|
|
|
|
for (index = 0; index < PCMRES_PCMCIA_MAX_IO; index++) {
|
|
|
|
if (scIoIndex >= MAX_NUMBER_OF_IO_RANGES) {
|
|
break;
|
|
}
|
|
|
|
socketConfig->Io[scIoIndex].Width16 = PCMRES_GET_IO_FLAG(partialResourceDesc, index, PCMRESF_IO_16BIT_ACCESS);
|
|
socketConfig->Io[scIoIndex].WaitState16 = PCMRES_GET_IO_FLAG(partialResourceDesc, index, PCMRESF_IO_WAIT_16);
|
|
socketConfig->Io[scIoIndex].Source16 = PCMRES_GET_IO_FLAG(partialResourceDesc, index, PCMRESF_IO_SOURCE_16);
|
|
socketConfig->Io[scIoIndex].ZeroWait8 = PCMRES_GET_IO_FLAG(partialResourceDesc, index, PCMRESF_IO_ZERO_WAIT_8);
|
|
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "PcCardConfig IO%d - Width:%d, Wait16:%d, Source16:%d, ZeroWait8:%d\n", scIoIndex,
|
|
socketConfig->Io[scIoIndex].Width16,
|
|
socketConfig->Io[scIoIndex].WaitState16,
|
|
socketConfig->Io[scIoIndex].Source16,
|
|
socketConfig->Io[scIoIndex].ZeroWait8));
|
|
|
|
scIoIndex++;
|
|
}
|
|
|
|
for (index = 0; index < PCMRES_PCMCIA_MAX_MEM; index++) {
|
|
|
|
if (scMemIndex >= MAX_NUMBER_OF_MEMORY_RANGES) {
|
|
break;
|
|
}
|
|
|
|
socketConfig->Memory[scMemIndex].Width16 = PCMRES_GET_MEMORY_FLAG(partialResourceDesc, index, PCMRESF_MEM_16BIT_ACCESS);
|
|
socketConfig->Memory[scMemIndex].WaitState = PCMRES_GET_MEMORY_WAITSTATES(partialResourceDesc, index);
|
|
socketConfig->Memory[scMemIndex].IsAttribute = PCMRES_GET_MEMORY_FLAG(partialResourceDesc, index, PCMRESF_MEM_ATTRIBUTE);
|
|
socketConfig->Memory[scMemIndex].CardBase = PCMRES_GET_MEMORY_CARDBASE(partialResourceDesc, index);
|
|
|
|
DebugPrint((PCMCIA_DEBUG_RESOURCES, "PcCardConfig MEM%d - Width:%d, Wait:%d, IsAttr:%d, CardBase:%x\n", scMemIndex,
|
|
socketConfig->Memory[scMemIndex].Width16,
|
|
socketConfig->Memory[scMemIndex].WaitState,
|
|
socketConfig->Memory[scMemIndex].IsAttribute,
|
|
socketConfig->Memory[scMemIndex].CardBase));
|
|
|
|
scMemIndex++;
|
|
}
|
|
|
|
|
|
|
|
} else if (PCMRES_GET_DESCRIPTOR_TYPE (partialResourceDesc) == DPTYPE_PCMCIA_MF_CONFIGURATION) {
|
|
//
|
|
// Multifunction configuration private
|
|
//
|
|
UCHAR IoResourceIndex;
|
|
|
|
fnConfig = ExAllocatePool(NonPagedPool, sizeof(FUNCTION_CONFIGURATION));
|
|
if (!fnConfig) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(fnConfig, sizeof(FUNCTION_CONFIGURATION));
|
|
|
|
fnConfig->ConfigRegisterBase = PCMRES_GET_CONFIG_REGISTER_BASE(partialResourceDesc);
|
|
fnConfig->ConfigOptions = PCMRES_GET_CONFIG_OPTIONS(partialResourceDesc);
|
|
if (PCMRES_GET_AUDIO_ENABLE(partialResourceDesc)) {
|
|
fnConfig->ConfigFlags = 0x08;
|
|
}
|
|
|
|
if (fnConfig->ConfigOptions & 0x02) {
|
|
IoResourceIndex = PCMRES_GET_PORT_RESOURCE_INDEX(partialResourceDesc);
|
|
if ((IoResourceIndex < partialResourceList->Count) &&
|
|
(partialResourceList->PartialDescriptors[IoResourceIndex].Type == CmResourceTypePort)) {
|
|
|
|
fnConfig->IoLimit = (UCHAR) (partialResourceList->PartialDescriptors[IoResourceIndex].u.Port.Length-1);
|
|
fnConfig->IoBase = partialResourceList->PartialDescriptors[IoResourceIndex].u.Port.Start.LowPart;
|
|
}
|
|
}
|
|
|
|
if (socketConfig->FunctionConfiguration == NULL) {
|
|
//
|
|
// This is the first MfConfig
|
|
//
|
|
socketConfig->FunctionConfiguration = fnConfig;
|
|
} else {
|
|
//
|
|
// Chain it on the end so it is fifo
|
|
//
|
|
PFUNCTION_CONFIGURATION mfTmp = socketConfig->FunctionConfiguration;
|
|
while (mfTmp->Next != NULL) {
|
|
mfTmp = mfTmp->Next;
|
|
}
|
|
mfTmp->Next = fnConfig;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
DebugPrint((PCMCIA_DEBUG_INFO, "PcmciaStartPcCard:Unknown resource type %d handed down",
|
|
(ULONG) partialResourceDesc->Type));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Power up the card if it isn't already..
|
|
//
|
|
status = PcmciaRequestSocketPower(pdoExtension, NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ASSERT(NT_SUCCESS(status));
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Turn on ZV for this card, if it needs it
|
|
//
|
|
if (socketData->Flags & SDF_ZV) {
|
|
PcmciaSetZV(fdoExtension, socket, TRUE);
|
|
SetSocketFlag(socket, SOCKET_CUSTOM_INTERFACE);
|
|
} else if (IsSocketFlagSet(socket, SOCKET_CUSTOM_INTERFACE)) {
|
|
PcmciaSetZV(fdoExtension, socket, FALSE);
|
|
ResetSocketFlag(socket, SOCKET_CUSTOM_INTERFACE);
|
|
}
|
|
|
|
PcmciaSetAudio(fdoExtension, socket, IsDeviceFlagSet(pdoExtension, PCMCIA_PDO_ENABLE_AUDIO));
|
|
|
|
pdoExtension->SocketConfiguration = socketConfig;
|
|
if (!NT_SUCCESS(PcmciaConfigurePcCard(pdoExtension, NULL))) {
|
|
//
|
|
// Problems in configuring the card: could be the card
|
|
// was removed while configuring it
|
|
//
|
|
pdoExtension->SocketConfiguration = NULL;
|
|
ExFreePool(socketConfig);
|
|
return STATUS_DEVICE_NOT_READY;
|
|
}
|
|
|
|
MarkDeviceStarted(pdoExtension);
|
|
MarkDeviceLogicallyInserted(pdoExtension);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PcmciaStopPcCard(
|
|
IN PDEVICE_OBJECT Pdo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine stops and deconfigures the given PC-Card
|
|
|
|
Arguments:
|
|
|
|
Pdo - Pointer to the device object representing the PC-Card which needs to be stopped
|
|
|
|
Return value:
|
|
|
|
STATUS_SUCCESS - PC-Card was already stopped, or stopped and deconfigured now successfully
|
|
|
|
--*/
|
|
{
|
|
PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension;
|
|
PSOCKET socket = pdoExtension->Socket;
|
|
PFDO_EXTENSION fdoExtension = socket->DeviceExtension;
|
|
CARD_REQUEST cardRequest;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!IsDeviceStarted(pdoExtension)) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
//
|
|
// Need to deconfigure the controller
|
|
//
|
|
PcmciaSocketDeconfigure(socket);
|
|
(socket->SocketFnPtr->PCBInitializePcmciaSocket)(socket);
|
|
|
|
MarkDeviceNotStarted(pdoExtension);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
PcmciaRemovePcCard(
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return value:
|
|
|
|
--*/
|
|
{
|
|
PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension;
|
|
PSOCKET socket = pdoExtension->Socket;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (socket == NULL) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PcmciaStopPcCard(Pdo);
|
|
PcmciaReleaseSocketPower(pdoExtension, NULL);
|
|
|
|
if (IsDevicePhysicallyRemoved(pdoExtension)) {
|
|
PFDO_EXTENSION fdoExtension = socket->DeviceExtension;
|
|
PDEVICE_OBJECT curPdo, prevPdo;
|
|
PPDO_EXTENSION curPdoExt;
|
|
ULONG waitCount = 0;
|
|
|
|
//
|
|
// Synchronize with power routines
|
|
// LATER: make these values adjustable
|
|
//
|
|
while(!PCMCIA_TEST_AND_SET(&pdoExtension->DeletionLock)) {
|
|
PcmciaWait(1000000);
|
|
if (waitCount++ > 20) {
|
|
ASSERT(waitCount <= 20);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Delink this Pdo from the FDO list.
|
|
//
|
|
for (curPdo = fdoExtension->PdoList, prevPdo = NULL; curPdo!=NULL; prevPdo = curPdo, curPdo=curPdoExt->NextPdoInFdoChain) {
|
|
curPdoExt = curPdo->DeviceExtension;
|
|
|
|
if (curPdo == Pdo) {
|
|
if (prevPdo) {
|
|
((PPDO_EXTENSION)prevPdo->DeviceExtension)->NextPdoInFdoChain = pdoExtension->NextPdoInFdoChain;
|
|
} else {
|
|
fdoExtension->PdoList = pdoExtension->NextPdoInFdoChain;
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
for (curPdo = socket->PdoList, prevPdo = NULL; curPdo!=NULL; prevPdo = curPdo, curPdo=curPdoExt->NextPdoInSocket) {
|
|
curPdoExt = curPdo->DeviceExtension;
|
|
|
|
if (curPdo == Pdo) {
|
|
//
|
|
// Delink this Pdo from the socket list.
|
|
//
|
|
if (prevPdo) {
|
|
((PPDO_EXTENSION)prevPdo->DeviceExtension)->NextPdoInSocket = pdoExtension->NextPdoInSocket;
|
|
} else {
|
|
socket->PdoList = pdoExtension->NextPdoInSocket;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
PcmciaCleanupSocketConfiguration(pdoExtension);
|
|
PcmciaCleanupPdo(Pdo);
|
|
//
|
|
// Delete..
|
|
//
|
|
if (!IsDeviceDeleted(pdoExtension)) {
|
|
MarkDeviceDeleted(pdoExtension);
|
|
IoDeleteDevice(Pdo);
|
|
}
|
|
|
|
ResetSocketFlag(socket, SOCKET_CLEANUP_PENDING);
|
|
//
|
|
// If a query_device_relations came in after a card was inserted, but before
|
|
// we have removed the previous card configuration, the enumeration would have been
|
|
// postponed. Here, we start it up again
|
|
//
|
|
if (IsSocketFlagSet(socket, SOCKET_ENUMERATE_PENDING)) {
|
|
ResetSocketFlag(socket, SOCKET_ENUMERATE_PENDING);
|
|
SetSocketFlag(socket, SOCKET_CARD_STATUS_CHANGE);
|
|
IoInvalidateDeviceRelations(fdoExtension->Pdo, BusRelations);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// We will keep this Pdo around, since this is not physically ejected.
|
|
//
|
|
MarkDeviceLogicallyRemoved(pdoExtension);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
PcmciaCleanupPdo(
|
|
IN PDEVICE_OBJECT Pdo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return value:
|
|
|
|
--*/
|
|
{
|
|
PPDO_EXTENSION pdoExtension;
|
|
PSOCKET_DATA socketData, tmpSocketData;
|
|
|
|
ASSERT (Pdo != NULL);
|
|
|
|
pdoExtension = Pdo->DeviceExtension;
|
|
|
|
ASSERT(pdoExtension->WaitWakeIrp == NULL);
|
|
|
|
if (pdoExtension->LowerDevice!=NULL) {
|
|
//
|
|
// Detach our filter device
|
|
//
|
|
IoDetachDevice(pdoExtension->LowerDevice);
|
|
pdoExtension->LowerDevice = NULL;
|
|
}
|
|
|
|
socketData = pdoExtension->SocketData;
|
|
pdoExtension->SocketData = NULL;
|
|
while (socketData != NULL) {
|
|
tmpSocketData = socketData;
|
|
socketData = socketData->Next;
|
|
PcmciaCleanupSocketData(tmpSocketData);
|
|
}
|
|
|
|
PcmciaCleanupSocketConfiguration(pdoExtension);
|
|
|
|
//
|
|
// Cleanup device id
|
|
//
|
|
if (pdoExtension->DeviceId) {
|
|
ExFreePool(pdoExtension->DeviceId);
|
|
pdoExtension->DeviceId = NULL;
|
|
}
|
|
|
|
if (pdoExtension->CisCache) {
|
|
ExFreePool(pdoExtension->CisCache);
|
|
pdoExtension->CisCache = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
PcmciaCleanupSocketData(
|
|
IN PSOCKET_DATA SocketData
|
|
)
|
|
/*++
|
|
|
|
Routine Descrption
|
|
|
|
Frees up the passed in SocketData structure & any
|
|
structures it might point to
|
|
|
|
Arguments
|
|
|
|
SocketData - Pointer to the SOCKET_DATA structure
|
|
|
|
Return Value
|
|
|
|
none
|
|
|
|
|
|
--*/
|
|
{
|
|
PCONFIG_ENTRY configEntry, nextConfigEntry;
|
|
|
|
if (SocketData == NULL) {
|
|
return;
|
|
}
|
|
//
|
|
// Free up the config entry descriptors
|
|
//
|
|
configEntry = SocketData->ConfigEntryChain;
|
|
SocketData->ConfigEntryChain = NULL;
|
|
while (configEntry) {
|
|
nextConfigEntry = configEntry->NextEntry;
|
|
ExFreePool(configEntry);
|
|
configEntry = nextConfigEntry;
|
|
}
|
|
|
|
//
|
|
// Free up socket data
|
|
//
|
|
ExFreePool(SocketData);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
PcmciaCleanupSocketConfiguration(
|
|
PPDO_EXTENSION pdoExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees up the linked list of function configuration, as well as
|
|
the base socket configuration structure itself.
|
|
|
|
Arguments:
|
|
|
|
Return value:
|
|
|
|
--*/
|
|
{
|
|
PSOCKET_CONFIGURATION socketConfig = pdoExtension->SocketConfiguration;
|
|
PFUNCTION_CONFIGURATION fnConfig, fnConfigNext;
|
|
|
|
if (socketConfig == NULL) {
|
|
return;
|
|
}
|
|
|
|
fnConfig = socketConfig->FunctionConfiguration;
|
|
while(fnConfig) {
|
|
fnConfigNext = fnConfig->Next;
|
|
ExFreePool(fnConfig);
|
|
fnConfig = fnConfigNext;
|
|
}
|
|
|
|
ExFreePool(pdoExtension->SocketConfiguration);
|
|
pdoExtension->SocketConfiguration = NULL;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PcmciaPdoDeviceControl(
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return value:
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// No IOCTLs handled currently
|
|
//
|
|
UNREFERENCED_PARAMETER(Pdo);
|
|
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PcmciaPdoDeviceCapabilities(
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Obtains the device capabilities of the given pc-card.
|
|
|
|
If the pc-card is an R2 card (16-bit pc-card), the capabilities
|
|
are constructed from the parent PCMCIA controller's capabilities.
|
|
Finally the obtained capabilities are cached in the pc-card's device
|
|
extension for use in power management of the card.
|
|
|
|
Arguments:
|
|
|
|
Pdo - Pointer to the device object for the pc-card
|
|
Irp - Pointer to the query device capabilities Irp
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - Capabilities obtained and recorded in the passed in pointer
|
|
STATUS_INSUFFICIENT_RESOURCES - Could not allocate memory to cache the capabilities
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PDEVICE_CAPABILITIES capabilities = irpStack->Parameters.DeviceCapabilities.Capabilities;
|
|
PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension;
|
|
PDEVICE_CAPABILITIES busCapabilities = &pdoExtension->Socket->DeviceExtension->DeviceCapabilities;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// R2 card. Fill in the capabilities ourselves..
|
|
//
|
|
|
|
capabilities->Removable = TRUE;
|
|
capabilities->UniqueID = TRUE;
|
|
capabilities->EjectSupported = FALSE;
|
|
|
|
capabilities->Address = pdoExtension->Socket->RegisterOffset;
|
|
// Don't know the UINumber, just leave it alone
|
|
|
|
|
|
if (busCapabilities->DeviceState[PowerSystemWorking] != PowerDeviceUnspecified) {
|
|
capabilities->DeviceState[PowerSystemWorking] = busCapabilities->DeviceState[PowerSystemWorking];
|
|
capabilities->DeviceState[PowerSystemSleeping1] = busCapabilities->DeviceState[PowerSystemSleeping1];
|
|
capabilities->DeviceState[PowerSystemSleeping2] = busCapabilities->DeviceState[PowerSystemSleeping2];
|
|
capabilities->DeviceState[PowerSystemSleeping3] = busCapabilities->DeviceState[PowerSystemSleeping3];
|
|
capabilities->DeviceState[PowerSystemHibernate] = busCapabilities->DeviceState[PowerSystemHibernate];
|
|
capabilities->DeviceState[PowerSystemShutdown] = busCapabilities->DeviceState[PowerSystemShutdown];
|
|
|
|
capabilities->SystemWake = MIN(PowerSystemSleeping3, busCapabilities->SystemWake);
|
|
capabilities->DeviceWake = PowerDeviceD0; // don't rely on FDO mungeing in the right thing for r2 cards
|
|
capabilities->D1Latency = busCapabilities->D1Latency;
|
|
capabilities->D2Latency = busCapabilities->D2Latency;
|
|
capabilities->D3Latency = busCapabilities->D3Latency;
|
|
} else {
|
|
capabilities->DeviceState[PowerSystemWorking] = PowerDeviceD0;
|
|
capabilities->DeviceState[PowerSystemSleeping1] = PowerDeviceD3;
|
|
capabilities->DeviceState[PowerSystemSleeping2] = PowerDeviceD3;
|
|
capabilities->DeviceState[PowerSystemSleeping3] = PowerDeviceD3;
|
|
capabilities->DeviceState[PowerSystemHibernate] = PowerDeviceD3;
|
|
capabilities->DeviceState[PowerSystemShutdown] = PowerDeviceD3;
|
|
|
|
capabilities->SystemWake = PowerSystemUnspecified;
|
|
capabilities->DeviceWake = PowerDeviceD0; // don't rely on FDO mungeing in the right thing for r2 cards
|
|
capabilities->D1Latency = 0; // No latency - since we do nothing
|
|
capabilities->D2Latency = 0; //
|
|
capabilities->D3Latency = 100;
|
|
}
|
|
//
|
|
// Store these capabilities away..
|
|
//
|
|
|
|
RtlCopyMemory(&pdoExtension->DeviceCapabilities,
|
|
capabilities,
|
|
sizeof(DEVICE_CAPABILITIES));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|