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

1846 lines
51 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

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

/*++
Copyright (c) 1997-2000 Microsoft Corporation
Module Name:
socket.c
Abstract:
This module contains the socket functions of the pcmcia driver
Author:
Neil Sandlin (neilsa) 3-Mar-1999
Environment:
Kernel mode
Revision History :
--*/
#include "pch.h"
//
// Internal References
//
NTSTATUS
PcmciaQueueSocketPowerRequest(
IN PSOCKET Socket,
IN UCHAR InitialState,
IN PPCMCIA_COMPLETION_ROUTINE PowerCompletionRoutine,
IN PVOID Context
);
VOID
PcmciaConfigurationWorkerInitialization(
PPDO_EXTENSION pdoExtension
);
NTSTATUS
PcmciaConfigurePcCardMemIoWindows(
IN PSOCKET Socket,
IN PSOCKET_CONFIGURATION SocketConfig
);
NTSTATUS
PcmciaConfigurePcCardIrq(
IN PSOCKET Socket,
IN PSOCKET_CONFIGURATION SocketConfig
);
NTSTATUS
PcmciaConfigurePcCardRegisters(
PPDO_EXTENSION pdoExtension
);
VOID
PcmciaConfigureModemHack(
IN PSOCKET Socket,
IN PSOCKET_CONFIGURATION SocketConfig
);
BOOLEAN
PcmciaProcessConfigureRequest(
IN PFDO_EXTENSION DeviceExtension,
IN PSOCKET Socket,
IN PCARD_REQUEST CardConfigurationRequest
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PcmciaGetConfigData)
#endif
NTSTATUS
PcmciaRequestSocketPower(
IN PPDO_EXTENSION PdoExtension,
IN PPCMCIA_COMPLETION_ROUTINE PowerCompletionRoutine
)
/*++
Routine Description:
This routine maintains a reference count of how many devices have requested power.
When the count increments from zero to one, a call is made to actually turn power
on.
Arguments:
Socket - Pointer to the socket for which power is to be applied
PowerCompletionRoutine - routine to be called after configuration is complete
Return value:
status
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PSOCKET socket = PdoExtension->Socket;
PFDO_EXTENSION fdoExtension = socket->DeviceExtension;
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x request power\n", socket));
if (PCMCIA_TEST_AND_SET(&PdoExtension->SocketPowerRequested)) {
if (InterlockedIncrement(&socket->PowerRequests) == 1) {
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power requests now %d, status %08x\n", socket, socket->PowerRequests));
status = PcmciaSetSocketPower(socket, PowerCompletionRoutine, PdoExtension, TRUE);
}
}
return status;
}
NTSTATUS
PcmciaReleaseSocketPower(
IN PPDO_EXTENSION PdoExtension,
IN PPCMCIA_COMPLETION_ROUTINE PowerCompletionRoutine
)
/*++
Routine Description:
This routine maintains a reference count of how many devices have requested power.
When the count decrements from one to zero, a call is made to actually turn power
off.
Arguments:
Socket - Pointer to the socket for which power is to be removed
PowerCompletionRoutine - routine to be called after configuration is complete
Return value:
status
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PSOCKET socket = PdoExtension->Socket;
PFDO_EXTENSION fdoExtension = socket->DeviceExtension;
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x release power\n", socket));
if (PCMCIA_TEST_AND_RESET(&PdoExtension->SocketPowerRequested)) {
if (InterlockedDecrement(&socket->PowerRequests) == 0) {
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power requests now %d, status %08x\n", socket, socket->PowerRequests));
//
// Never actually turn off the drive rails for cardbus functions since
// we don't have tight integration with pci.sys, and config space will
// disappear
//
if (!IsCardBusCardInSocket(socket)) {
status = PcmciaSetSocketPower(socket, PowerCompletionRoutine, PdoExtension, FALSE);
}
}
ASSERT(socket->PowerRequests >= 0);
}
return status;
}
NTSTATUS
PcmciaSetSocketPower(
IN PSOCKET Socket,
IN PPCMCIA_COMPLETION_ROUTINE PowerCompletionRoutine,
IN PVOID Context,
IN BOOLEAN PowerOn
)
/*++
Routine Description:
This routine is entered when we know the power state of the socket will
actually be set.
NOTE: If this routine is called at less than DISPATCH_LEVEL, then the call
will complete (not return STATUS_PENDING). If this routine is called at
DISPATCH_LEVEL or greater, this routine returns STATUS_PENDING, and completes
the power process using a KTIMER.
Arguments:
Socket - Pointer to the socket for which power is to be removed
PowerCompletionRoutine - routine to be called after configuration is complete
Return value:
status
--*/
{
NTSTATUS status;
PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
SPW_STATE InitialState = PowerOn ? SPW_RequestPower : SPW_ReleasePower;
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x set power %s\n", Socket, PowerOn ? "ON" : "OFF"));
if (!PCMCIA_TEST_AND_SET(&Socket->WorkerBusy)) {
return STATUS_DEVICE_BUSY;
}
ASSERT(Socket->WorkerState == SPW_Stopped);
//
// committed, will enter SocketPowerWorker now
//
Socket->WorkerState = InitialState;
Socket->PowerCompletionRoutine = PowerCompletionRoutine;
Socket->PowerCompletionContext = Context;
PcmciaSocketPowerWorker(&Socket->PowerDpc, Socket, NULL, NULL);
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x SetSocketPower returning %08x\n",
Socket, Socket->CallerStatus));
return(Socket->CallerStatus);
}
VOID
PcmciaSocketPowerWorker(
IN PKDPC Dpc,
IN PVOID Context,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description
This routine handles the power up process of a socket. Because such
long delays are needed, and because this routine may be called at
raised irql, this is a state machine that has the capability of
calling itself on a KTIMER.
Arguments
same as KDPC (DeferredContext is socket)
Return Value
status
--*/
{
PSOCKET Socket = Context;
PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
NTSTATUS status = Socket->DeferredStatus;
ULONG DelayTime = 0;
BOOLEAN ContinueExecution = TRUE;
#if DBG
{
ULONG Phase = 0;
switch(Socket->WorkerState) {
case SPW_SetPowerOn:
case SPW_SetPowerOff:
Phase = Socket->PowerPhase;
break;
}
if (Phase) {
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power worker - %s(%d)\n", Socket,
SOCKET_POWER_WORKER_STRING(Socket->WorkerState), Phase));
} else {
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power worker - %s\n", Socket,
SOCKET_POWER_WORKER_STRING(Socket->WorkerState)));
}
}
#endif
//
// Socket power state machine
//
switch(Socket->WorkerState) {
case SPW_RequestPower:
status = STATUS_SUCCESS;
if (IsSocketFlagSet(Socket, SOCKET_CARD_POWERED_UP)) {
Socket->WorkerState = SPW_Exit;
} else {
if ((KeGetCurrentIrql() >= DISPATCH_LEVEL) && (Socket->PowerCompletionRoutine == NULL)) {
ASSERT((KeGetCurrentIrql() < DISPATCH_LEVEL) || (Socket->PowerCompletionRoutine != NULL));
//
// no completion routine at raised irql
//
status = STATUS_INVALID_PARAMETER;
} else {
//
// All ok, continue to next state
//
Socket->PowerPhase = 1;
Socket->WorkerState = SPW_SetPowerOn;
}
}
break;
case SPW_ReleasePower:
status = STATUS_SUCCESS;
if (!IsSocketFlagSet(Socket, SOCKET_CARD_POWERED_UP)) {
Socket->WorkerState = SPW_Exit;
} else {
if ((KeGetCurrentIrql() >= DISPATCH_LEVEL) && (Socket->PowerCompletionRoutine == NULL)) {
ASSERT((KeGetCurrentIrql() < DISPATCH_LEVEL) || (Socket->PowerCompletionRoutine != NULL));
//
// no completion routine at raised irql
//
status = STATUS_INVALID_PARAMETER;
} else {
//
// All ok, continue to next state
//
Socket->WorkerState = SPW_Deconfigure;
}
}
break;
case SPW_SetPowerOn:
//
// Turn power ON
//
status = (*(DeviceDispatchTable[fdoExtension->DeviceDispatchIndex].SetPower))
(Socket, TRUE, &DelayTime);
Socket->PowerPhase++;
if (status != STATUS_MORE_PROCESSING_REQUIRED) {
if (NT_SUCCESS(status)) {
//
// Done with power up, proceed to the init sequence
//
SetSocketFlag(Socket, SOCKET_CARD_POWERED_UP);
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power UP\n", Socket));
Socket->WorkerState = SPW_ResetCard;
Socket->CardResetPhase = 1;
} else if (status == STATUS_INVALID_DEVICE_STATE) {
//
// Power was already on, don't reset the card
//
SetSocketFlag(Socket, SOCKET_CARD_POWERED_UP);
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power already UP\n", Socket));
Socket->WorkerState = SPW_Exit;
status = STATUS_SUCCESS;
} else {
//
// Power didn't go on
//
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x poweron fail %08x\n", Socket, status));
Socket->WorkerState = SPW_Exit;
}
}
break;
case SPW_ResetCard:
//
// Make sure the card is ready to be enumerated
//
status = (*(Socket->SocketFnPtr->PCBResetCard))(Socket, &DelayTime);
Socket->CardResetPhase++;
if (status != STATUS_MORE_PROCESSING_REQUIRED) {
Socket->WorkerState = SPW_Exit;
}
break;
case SPW_Deconfigure:
PcmciaSocketDeconfigure(Socket);
Socket->PowerPhase = 1;
Socket->WorkerState = SPW_SetPowerOff;
break;
case SPW_SetPowerOff:
//
// Turn power OFF
//
status = (*(DeviceDispatchTable[fdoExtension->DeviceDispatchIndex].SetPower))
(Socket, FALSE, &DelayTime);
Socket->PowerPhase++;
if (status != STATUS_MORE_PROCESSING_REQUIRED) {
Socket->WorkerState = SPW_Exit;
if (NT_SUCCESS(status)) {
//
// Power is now off
//
ResetSocketFlag(Socket, SOCKET_CARD_POWERED_UP);
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power DOWN\n", Socket));
} else if (status == STATUS_INVALID_DEVICE_STATE) {
//
// Power was already off
//
ResetSocketFlag(Socket, SOCKET_CARD_POWERED_UP);
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power already DOWN\n", Socket));
status = STATUS_SUCCESS;
} else {
//
// Power didn't go off
//
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x poweroff fail %08x\n", Socket, status));
Socket->WorkerState = SPW_Exit;
}
}
break;
case SPW_Exit:
if (!NT_SUCCESS(status)) {
DebugPrint((PCMCIA_DEBUG_FAIL, "skt %08x SocketPowerWorker FAILED, status %08x\n", Socket, status));
ASSERT(NT_SUCCESS(status));
}
//
// Done. Update flags, and call the completion routine if required
//
if (PCMCIA_TEST_AND_RESET(&Socket->DeferredStatusLock)) {
PPCMCIA_COMPLETION_ROUTINE PowerCompletionRoutine = Socket->PowerCompletionRoutine;
PVOID PowerCompletionContext = Socket->PowerCompletionContext;
Socket->WorkerState = SPW_Stopped;
PCMCIA_TEST_AND_RESET(&Socket->WorkerBusy);
if (PowerCompletionRoutine) {
(*PowerCompletionRoutine)(PowerCompletionContext, status);
} else {
ASSERT(PowerCompletionRoutine != NULL);
}
} else {
Socket->CallerStatus = status;
Socket->WorkerState = SPW_Stopped;
PCMCIA_TEST_AND_RESET(&Socket->WorkerBusy);
}
return;
default:
ASSERT(FALSE);
return;
}
//
// Now check the results
//
if (status == STATUS_PENDING) {
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x worker exit, status pending\n", Socket));
//
// whatever returned pending will call us back
//
if (PCMCIA_TEST_AND_SET(&Socket->DeferredStatusLock)) {
//
// First time that we are waiting, we will return to original
// caller. So update the main power status just this time.
//
Socket->CallerStatus = STATUS_PENDING;
}
return;
}
//
// remember for next time
//
Socket->DeferredStatus = status;
if (!NT_SUCCESS(status) && (status != STATUS_MORE_PROCESSING_REQUIRED)) {
Socket->WorkerState = SPW_Exit;
DelayTime = 0;
}
//
// Not done yet. Recurse or call timer
//
if (DelayTime) {
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power worker delay type %s, %d usec\n", Socket,
(KeGetCurrentIrql() < DISPATCH_LEVEL) ? "Wait" : "Timer",
DelayTime));
if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
PcmciaWait(DelayTime);
} else {
LARGE_INTEGER dueTime;
//
// Running on a DPC, kick of a kernel timer
//
if (PCMCIA_TEST_AND_SET(&Socket->DeferredStatusLock)) {
//
// First time that we are waiting, we will return to original
// caller. So update the main power status just this time.
//
Socket->CallerStatus = STATUS_PENDING;
}
dueTime.QuadPart = -((LONG) DelayTime*10);
KeSetTimer(&Socket->PowerTimer, dueTime, &Socket->PowerDpc);
//
// We will reenter on timer dpc
//
return;
}
}
//
// Recurse
//
PcmciaSocketPowerWorker(&Socket->PowerDpc, Socket, NULL, NULL);
}
VOID
PcmciaGetSocketStatus(
IN PSOCKET Socket
)
/*++
Routine Description:
A small utility routine that returns some common socket flags. The reason
it exists is to allow the Enumerate Devices routine to remain pagable.
NOTE: This routine updates the "software view" of the device state. This
should only be done at well-defined points in the driver. In particular,
you do not want to be updating the software state immediately after
a surprise remove. Instead, most of the driver needs to continue to
believe the card is still there while it does its unconfigure and
poweroff.
Arguments:
Socket - The socket in which the PC-Card resides
boolean parameters are written according to socket flags
Return Value:
none
--*/
{
BOOLEAN isCardInSocket, isCardBusCard;
UCHAR previousDeviceState;
PCMCIA_ACQUIRE_DEVICE_LOCK(Socket->DeviceExtension);
isCardInSocket = (*(Socket->SocketFnPtr->PCBDetectCardInSocket))(Socket);
isCardBusCard = FALSE;
if (isCardInSocket && CardBus(Socket)) {
isCardBusCard = ((CBReadSocketRegister(Socket, CARDBUS_SOCKET_PRESENT_STATE_REG) & CARDBUS_CB_CARD) != 0);
}
previousDeviceState = Socket->DeviceState;
if (!isCardInSocket) {
SetSocketEmpty(Socket);
} else if (isCardBusCard) {
SetCardBusCardInSocket(Socket);
} else {
Set16BitCardInSocket(Socket);
}
if (previousDeviceState != Socket->DeviceState) {
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %x Socket Status: Card Status Change!\n", Socket));
SetSocketFlag(Socket, SOCKET_CARD_STATUS_CHANGE);
}
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %x Socket Status: %s\n",
Socket, isCardInSocket ? (isCardBusCard ? "INSERTED Cardbus" : "INSERTED R2") : "EMPTY"));
PCMCIA_RELEASE_DEVICE_LOCK(Socket->DeviceExtension);
//
// Fill in socket power values
//
if (isCardInSocket && IsSocketFlagSet(Socket, SOCKET_CARD_STATUS_CHANGE) && Socket->SocketFnPtr->PCBGetPowerRequirements) {
(*(Socket->SocketFnPtr->PCBGetPowerRequirements))(Socket);
}
}
NTSTATUS
PcmciaGetConfigData(
PPDO_EXTENSION PdoExtension
)
/*++
Routine Description:
This routine controls the translation of the CIS config data for the card into
SOCKET_DATA structures chained onto the PDO. The action of this routine depends
on the type of device:
1) For a standard R2 card, a single SOCKET_DATA structure is linked to the pdo
extension, which contains a straight translation of the CIS contents.
2) For a fully compliant true R2 MF card, a chain of SOCKET_DATA structures is
created, one for each function on the card.
3) For a non-conforming R2 MF card (the typical case), a single structure is
linked just like case #1.
4) For a CardBus card, a single SOCKET_DATA is linked to the pdo extension. If
there are multiple functions on the device, then there will be multiple pdo
extensions, each with a single SOCKET_DATA structure.
Arguments:
pdoExtension - The pdo extension corresponding to the specified pccard or cb function.
Return Value:
STATUS_SUCCESS
STATUS_NO_SUCH_DEVICE if no card is present in the socket (i.e. the passed in PDO is 'dead')
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PSOCKET_DATA socketData, prevSocketData;
PSOCKET_DATA socketDataList = NULL;
UCHAR function = 0;
PSOCKET Socket = PdoExtension->Socket;
PAGED_CODE ();
if (!IsCardInSocket(Socket)) {
//
// Card probably removed,
// and Pdo's ghost still hanging around
//
return STATUS_NO_SUCH_DEVICE;
}
ResetSocketFlag(Socket, SOCKET_CARD_MEMORY);
ResetSocketFlag(Socket, SOCKET_CARD_CONFIGURED);
DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x performing GetSocketData\n", Socket));
Socket->NumberOfFunctions = 1;
prevSocketData = NULL;
while (function < 255) {
//
// Parse tuples of next function on the card
//
socketData = ExAllocatePool(NonPagedPool, sizeof(SOCKET_DATA));
if (socketData == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
RtlZeroMemory(socketData, sizeof(SOCKET_DATA));
socketData->Function = function;
socketData->PdoExtension = PdoExtension;
socketData->Socket = Socket;
DebugPrint((PCMCIA_DEBUG_SOCKET, "Parsing function %d...\n", socketData->Function));
status = PcmciaParseFunctionData(Socket, socketData);
if (NT_SUCCESS(status)) {
//
// Link it to the list of socketdata structures
//
socketData->Prev = prevSocketData;
socketData->Next = NULL;
if (prevSocketData) {
prevSocketData->Next = socketData;
} else {
//
// This is the first function on the card
// Make it the head of the list
//
socketDataList = socketData;
}
if (socketData->DeviceType == PCCARD_TYPE_MODEM) {
SetDeviceFlag(PdoExtension, PCMCIA_PDO_ENABLE_AUDIO);
}
} else {
//
// no more functions on this card
//
ExFreePool(socketData);
if ((function > 0) && (status == STATUS_NO_MORE_ENTRIES)) {
status = STATUS_SUCCESS;
}
break;
}
function++;
prevSocketData = socketData;
}
if (!NT_SUCCESS(status)) {
socketData = socketDataList;
while(socketData) {
prevSocketData = socketData;
socketData = socketData->Next;
ExFreePool(prevSocketData);
}
} else {
PdoExtension->SocketData = socketDataList;
}
return status;
}
UCHAR
PcmciaReadCISChar(
PPDO_EXTENSION PdoExtension,
IN MEMORY_SPACE MemorySpace,
IN ULONG Offset
)
/*++
Routine Description:
Returns the card data. This information is cached in the socket
structure. This way once a PCCARD is enabled it will not be touched
due to a query ioctl.
Arguments:
Context
Return Value:
TRUE
--*/
{
PSOCKET socket = PdoExtension->Socket;
PDEVICE_OBJECT pdo;
UCHAR retValue = 0xff;
ULONG relativeOffset;
ULONG bytesRead;
if (socket && IsCardInSocket(socket)) {
if (!PdoExtension->CisCache) {
#define PCMCIA_CIS_CACHE_SIZE 2048
PdoExtension->CisCache = ExAllocatePool(NonPagedPool, PCMCIA_CIS_CACHE_SIZE);
PdoExtension->CisCacheSpace = 0xff;
PdoExtension->CisCacheBase = 0;
}
if (PdoExtension->CisCache) {
if ((MemorySpace != PdoExtension->CisCacheSpace) ||
(Offset < PdoExtension->CisCacheBase) ||
(Offset > PdoExtension->CisCacheBase + PCMCIA_CIS_CACHE_SIZE - 1)) {
//
// LATER: If devices have CIS > CacheSize, then we should window this
//
bytesRead = (*(socket->SocketFnPtr->PCBReadCardMemory))(PdoExtension,
MemorySpace,
0,
PdoExtension->CisCache,
PCMCIA_CIS_CACHE_SIZE);
PdoExtension->CisCacheSpace = MemorySpace;
}
relativeOffset = Offset - PdoExtension->CisCacheBase;
if (relativeOffset < PCMCIA_CIS_CACHE_SIZE) {
retValue = PdoExtension->CisCache[relativeOffset];
}
}
}
return retValue;
}
NTSTATUS
PcmciaReadWriteCardMemory(
IN PDEVICE_OBJECT Pdo,
IN ULONG WhichSpace,
IN OUT PUCHAR Buffer,
IN ULONG Offset,
IN ULONG Length,
IN BOOLEAN Read
)
/*++
Routine Description:
This routine is to provide IRP_MN_READ_CONFIG/WRITE_CONFIG support: this would locate the
socket on which Pdo resides and map the card's memory into the system space.
If Read is TRUE it would:
copy the contents of the config memory at a specified offset and length to the
caller supplied buffer.
else
copy the contents of the caller specified buffer at the specified offset and length of
the config memory
Note: this has to be non-paged since it can be called by
clients at DISPATCH_LEVEL
Arguments:
Pdo - Device object representing the PC-CARD whose config memory needs to be read/written
WhichSpace - Indicates which memory space needs to be mapped: one of
PCCARD_COMMON_MEMORY_SPACE
PCCARD_ATTRIBUTE_MEMORY_SPACE
PCCARD_PCI_CONFIGURATION_MEMORY_SPACE (only for cardbus cards)
Buffer - Caller supplied buffer into/out of which the memory contents are copied
Offset - Offset of the attribute memory at which we copy
Length - Number of bytes of attribute memory/buffer to be copied
Return value:
STATUS_INVALID_PARAMETER_1
STATUS_INVALID_PARAMETER_2
STATUS_INVALID_PARAMETER_3 If supplied parameters are not valid
STATUS_NO_SUCH_DEVICE No PC-Card in the socket
STATUS_DEVICE_NOT_READY PC-Card not initialized yet or some other hardware related error
STATUS_SUCCESS Contents copied as requested
--*/
{
PSOCKET socket;
PSOCKET_DATA socketData;
PUCHAR tupleData;
ULONG tupleDataSize;
PPDO_EXTENSION pdoExtension;
NTSTATUS status = STATUS_UNSUCCESSFUL;
pdoExtension = Pdo->DeviceExtension;
socket= pdoExtension->Socket;
//
// Got to have a card in the socket to read/write from it..
//
if (!IsCardInSocket(socket)) {
return STATUS_NO_SUCH_DEVICE;
}
//
// Memory space has to be one of the defined ones.
//
if ((WhichSpace != PCCARD_COMMON_MEMORY) &&
(WhichSpace != PCCARD_ATTRIBUTE_MEMORY) &&
(WhichSpace != PCCARD_PCI_CONFIGURATION_SPACE)) {
return STATUS_INVALID_PARAMETER_1;
}
//
// We support PCCARD_PCI_CONFIGURATION_SPACE only
// for cardbus cards (doesn't make sense for R2 cards)
// Similarily PCCARD_ATTRIBUTE/COMMON_MEMORY only for
// R2 cards
//
if ((((WhichSpace == PCCARD_ATTRIBUTE_MEMORY) ||
(WhichSpace == PCCARD_COMMON_MEMORY)) && !Is16BitCard(pdoExtension)) ||
((WhichSpace == PCCARD_PCI_CONFIGURATION_SPACE) && !IsCardBusCard(pdoExtension))) {
return STATUS_INVALID_PARAMETER_1;
}
if (!Buffer) {
return STATUS_INVALID_PARAMETER_2;
}
if (WhichSpace == PCCARD_PCI_CONFIGURATION_SPACE) {
//
// This has to be a cardbus card.
//
// NOTE: unimplemented: fill this in! send an IRP down to PCI
// to get the config space
status = STATUS_NOT_SUPPORTED;
} else {
//
// This has to be an R2 Card.
// Attribute/common memory space
//
ASSERT ((WhichSpace == PCCARD_ATTRIBUTE_MEMORY) ||
(WhichSpace == PCCARD_COMMON_MEMORY));
//
// Offset and length are >= 0 because they are ULONGs,
// so don't worry about that.
//
if (!IsSocketFlagSet(socket, SOCKET_CARD_POWERED_UP)) {
return STATUS_DEVICE_NOT_READY;
}
PCMCIA_ACQUIRE_DEVICE_LOCK(socket->DeviceExtension);
if (Read && (socket->SocketFnPtr->PCBReadCardMemory != NULL)) {
//
// Read from card memory
//
status = ((*(socket->SocketFnPtr->PCBReadCardMemory))(pdoExtension,
WhichSpace,
Offset,
Buffer,
Length) == Length)
? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
} else if (socket->SocketFnPtr->PCBWriteCardMemory != NULL) {
//
// Write to card memory
//
status = ((*(socket->SocketFnPtr->PCBWriteCardMemory))(pdoExtension,
WhichSpace,
Offset,
Buffer,
Length) == Length)
? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
}
PCMCIA_RELEASE_DEVICE_LOCK(socket->DeviceExtension);
}
return status;
}
NTSTATUS
PcmciaConfigureCardBusCard(
PPDO_EXTENSION pdoExtension
)
/*++
Routine Description:
This routine does some verification, and applies hacks
Arguments:
pdoExtension - Pointer to the physical device object extension for the pc-card
Return value:
status
--*/
{
ULONG i, pciConfig;
NTSTATUS status = STATUS_SUCCESS;
PSOCKET Socket = pdoExtension->Socket;
for (i = 0; i < CARDBUS_CONFIG_RETRY_COUNT; i++) {
GetPciConfigSpace(pdoExtension, CFGSPACE_VENDOR_ID, &pciConfig, sizeof(pciConfig));
if (pciConfig != 0xffffffff) {
break;
}
}
if (pciConfig == 0xffffffff) {
DebugPrint((PCMCIA_DEBUG_FAIL, "pdo %08x failed to verify CardBus config space\n", pdoExtension->DeviceObject));
status = STATUS_DEVICE_NOT_READY;
} else {
PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
//
// The TI1130, 1131, 1031 have a bug such that CAUDIO on a cardbus card
// is gated by the following bit (which normally only has meaning only
// for R2 cards). We can workaround the problem simply by turning it on
// for cardbus cards.
//
if ((fdoExtension->ControllerType == PcmciaTI1130) ||
(fdoExtension->ControllerType == PcmciaTI1131) ||
(fdoExtension->ControllerType == PcmciaTI1031)) {
UCHAR byte;
byte = PcicReadSocket(Socket, PCIC_INTERRUPT);
byte |= IGC_PCCARD_IO;
PcicWriteSocket(Socket, PCIC_INTERRUPT, byte);
}
}
return status;
}
NTSTATUS
PcmciaConfigurePcCard(
PPDO_EXTENSION pdoExtension,
IN PPCMCIA_COMPLETION_ROUTINE ConfigCompletionRoutine
)
/*++
Routine Description:
This routine does the brunt work of enabling the PC-Card using the supplied
resources.
NOTE: If this routine is called at less than DISPATCH_LEVEL, then the call
will complete (not return STATUS_PENDING). If this routine is called at
DISPATCH_LEVEL or greater, this routine returns STATUS_PENDING, and completes
the configuration process using a KTIMER.
Arguments:
pdoExtension - Pointer to the physical device object extension for the pc-card
ConfigCompletionRoutine - routine to be called after configuration is complete
Return value:
status
--*/
{
DebugPrint((PCMCIA_DEBUG_CONFIG, "pdo %08x ConfigurePcCard entered\n", pdoExtension->DeviceObject));
if (!PCMCIA_TEST_AND_SET(&pdoExtension->Socket->WorkerBusy)) {
return STATUS_DEVICE_BUSY;
}
ASSERT(pdoExtension->ConfigurationPhase == CW_Stopped);
pdoExtension->ConfigurationPhase = CW_InitialState;
pdoExtension->ConfigCompletionRoutine = ConfigCompletionRoutine;
pdoExtension->ConfigurationStatus = STATUS_SUCCESS;
PcmciaConfigurationWorker(&pdoExtension->ConfigurationDpc, pdoExtension, NULL, NULL);
DebugPrint((PCMCIA_DEBUG_CONFIG, "pdo %08x ConfigurePcCard returning %08x\n", pdoExtension->DeviceObject, pdoExtension->ConfigurationStatus));
return(pdoExtension->ConfigurationStatus);
}
VOID
PcmciaConfigurationWorker(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description
This routine handles the configuration process of a 16-bit R2 pccard.
Because certain cards are finicky (modems), and require gigantic pauses
between steps, this worker routine acts as a state machine, and will
delay after each step.
Arguments
same as KDPC (DeferredContext is pdoExtension)
Return Value
status
--*/
{
PPDO_EXTENSION pdoExtension = DeferredContext;
PSOCKET Socket = pdoExtension->Socket;
PSOCKET_CONFIGURATION SocketConfig = pdoExtension->SocketConfiguration;
NTSTATUS status = pdoExtension->DeferredConfigurationStatus;
ULONG DelayUsec = 0;
DebugPrint((PCMCIA_DEBUG_CONFIG, "pdo %08x config worker - %s\n", pdoExtension->DeviceObject,
CONFIGURATION_WORKER_STRING(pdoExtension->ConfigurationPhase)));
switch(pdoExtension->ConfigurationPhase) {
case CW_InitialState:
if (IsSocketFlagSet(pdoExtension->Socket, SOCKET_CARD_CONFIGURED)) {
pdoExtension->ConfigurationPhase = CW_Exit;
break;
}
if (!IsCardInSocket(pdoExtension->Socket)) {
status = STATUS_NO_SUCH_DEVICE;
pdoExtension->ConfigurationPhase = CW_Exit;
break;
}
pdoExtension->ConfigurationPhase = CW_ResetCard;
Socket->CardResetPhase = 1;
break;
case CW_ResetCard:
//
// Reset the card
//
status = (*(Socket->SocketFnPtr->PCBResetCard))(Socket, &DelayUsec);
Socket->CardResetPhase++;
if (status != STATUS_MORE_PROCESSING_REQUIRED) {
pdoExtension->ConfigurationPhase = CW_Phase1;
}
break;
case CW_Phase1:
//
// Initialize variables
//
PcmciaConfigurationWorkerInitialization(pdoExtension);
//
// Configure the cards configuration registers, and the socket mem
// and I/O windows
//
status = PcmciaConfigurePcCardRegisters(pdoExtension);
if (NT_SUCCESS(status)) {
status = PcmciaConfigurePcCardMemIoWindows(Socket, SocketConfig);
}
DelayUsec = 1000 * (ULONG)pdoExtension->ConfigureDelay1;
pdoExtension->ConfigurationPhase = CW_Phase2;
break;
case CW_Phase2:
//
// Take this opportunity to "poke" the modem
//
if (pdoExtension->ConfigurationFlags & CONFIG_WORKER_APPLY_MODEM_HACK) {
PcmciaConfigureModemHack(Socket, SocketConfig);
}
DelayUsec = 1000 * (ULONG)pdoExtension->ConfigureDelay2;
pdoExtension->ConfigurationPhase = CW_Phase3;
break;
case CW_Phase3:
//
// Configure the IRQ
//
status = PcmciaConfigurePcCardIrq(Socket, SocketConfig);
DelayUsec = 1000 * (ULONG)pdoExtension->ConfigureDelay3;
pdoExtension->ConfigurationPhase = CW_Exit;
break;
case CW_Exit:
//
// Done. Update flags, and call the completion routine if required
//
if (IsDeviceFlagSet(pdoExtension, PCMCIA_CONFIG_STATUS_DEFERRED)) {
if (pdoExtension->ConfigCompletionRoutine) {
(*pdoExtension->ConfigCompletionRoutine)(pdoExtension,
pdoExtension->DeferredConfigurationStatus);
}
ResetDeviceFlag(pdoExtension, PCMCIA_CONFIG_STATUS_DEFERRED);
} else {
pdoExtension->ConfigurationStatus = status;
}
if (NT_SUCCESS(status)) {
SetSocketFlag(Socket, SOCKET_CARD_CONFIGURED);
}
pdoExtension->ConfigurationPhase = CW_Stopped;
PCMCIA_TEST_AND_RESET(&Socket->WorkerBusy);
DebugPrint((PCMCIA_DEBUG_CONFIG, "pdo %08x config worker exit %08x\n", pdoExtension->DeviceObject, status));
return;
default:
ASSERT(FALSE);
return;
}
pdoExtension->DeferredConfigurationStatus = status;
if (!NT_SUCCESS(status) && (status != STATUS_MORE_PROCESSING_REQUIRED)) {
DelayUsec = 0;
pdoExtension->ConfigurationPhase = CW_Exit;
}
//
// Not done yet. Recurse or call timer
//
if (DelayUsec) {
DebugPrint((PCMCIA_DEBUG_CONFIG, "pdo %08x config worker delay type %s, %d usec\n",
pdoExtension->DeviceObject,
(KeGetCurrentIrql() < DISPATCH_LEVEL) ? "Wait" : "Timer",
DelayUsec));
if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
PcmciaWait(DelayUsec);
} else {
LARGE_INTEGER dueTime;
dueTime.QuadPart = -((LONG) DelayUsec*10);
//
// Running on a DPC, kick of a kernel timer
//
KeSetTimer(&pdoExtension->ConfigurationTimer,
dueTime,
&pdoExtension->ConfigurationDpc);
if (!IsDeviceFlagSet(pdoExtension, PCMCIA_CONFIG_STATUS_DEFERRED)) {
SetDeviceFlag(pdoExtension, PCMCIA_CONFIG_STATUS_DEFERRED);
pdoExtension->ConfigurationStatus = STATUS_PENDING;
}
return;
}
}
PcmciaConfigurationWorker(&pdoExtension->ConfigurationDpc, pdoExtension, NULL, NULL);
}
VOID
PcmciaConfigurationWorkerInitialization(
PPDO_EXTENSION pdoExtension
)
/*++
Routine Description:
This routine sets variables which control the configuration process.
Arguments:
pdoExtension - Pointer to the physical device object extension for the pc-card
Return value:
none
--*/
{
PSOCKET_DATA socketData = pdoExtension->SocketData;
ULONG i;
pdoExtension->ConfigurationFlags = 0;
pdoExtension->ConfigureDelay1 = 0;
pdoExtension->ConfigureDelay2 = 0;
pdoExtension->ConfigureDelay3 = 0;
while (socketData) {
i = 0;
while (DeviceConfigParams[i].ValidEntry) {
if (((DeviceConfigParams[i].DeviceType == 0xff) ||
(DeviceConfigParams[i].DeviceType == socketData->DeviceType)) &&
((DeviceConfigParams[i].ManufacturerCode == 0xffff) ||
(DeviceConfigParams[i].ManufacturerCode == socketData->ManufacturerCode)) &&
((DeviceConfigParams[i].ManufacturerInfo == 0xffff) ||
(DeviceConfigParams[i].ManufacturerInfo == socketData->ManufacturerInfo)) &&
((DeviceConfigParams[i].CisCrc == 0xffff) ||
(DeviceConfigParams[i].CisCrc == socketData->CisCrc))) {
pdoExtension->ConfigurationFlags = DeviceConfigParams[i].ConfigFlags;
pdoExtension->ConfigureDelay1 = DeviceConfigParams[i].ConfigDelay1;
pdoExtension->ConfigureDelay2 = DeviceConfigParams[i].ConfigDelay2;
pdoExtension->ConfigureDelay3 = DeviceConfigParams[i].ConfigDelay3;
break;
}
i++;
}
socketData = socketData->Next;
}
}
NTSTATUS
PcmciaConfigurePcCardMemIoWindows(
IN PSOCKET Socket,
IN PSOCKET_CONFIGURATION SocketConfig
)
/*++
Routine Description:
This routine enables the socket memory and I/O windows
Arguments:
Socket - Pointer to the socket containing the PC-Card
SocketConfig - Pointer to the socket configuration structure which contains the
resources required to enable this pc-card
Return value:
status
--*/
{
CARD_REQUEST cardRequest = {0};
PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
NTSTATUS status = STATUS_SUCCESS;
ULONG i;
DebugPrint((PCMCIA_DEBUG_CONFIG, "socket %08x config MemIo\n", Socket));
//
// Setup IO ranges if there are any
//
if (SocketConfig->NumberOfIoPortRanges) {
cardRequest.RequestType = IO_REQUEST;
cardRequest.u.Io.NumberOfRanges = (USHORT) SocketConfig->NumberOfIoPortRanges;
for (i = 0; i < SocketConfig->NumberOfIoPortRanges; i++) {
DebugPrint((PCMCIA_DEBUG_CONFIG, "\tport range: 0x%x-0x%x\n",
SocketConfig->Io[i].Base,
SocketConfig->Io[i].Base + SocketConfig->Io[i].Length));
cardRequest.u.Io.IoEntry[i].BasePort = SocketConfig->Io[i].Base;
cardRequest.u.Io.IoEntry[i].NumPorts = SocketConfig->Io[i].Length;
cardRequest.u.Io.IoEntry[i].Attributes = 0;
if (SocketConfig->Io[i].Width16) {
cardRequest.u.Io.IoEntry[i].Attributes |= IO_DATA_PATH_WIDTH;
}
if (SocketConfig->Io[i].WaitState16) {
cardRequest.u.Io.IoEntry[i].Attributes |= IO_WAIT_STATE_16;
}
if (SocketConfig->Io[i].Source16) {
cardRequest.u.Io.IoEntry[i].Attributes |= IO_SOURCE_16;
}
if (SocketConfig->Io[i].ZeroWait8) {
cardRequest.u.Io.IoEntry[i].Attributes |= IO_ZERO_WAIT_8;
}
}
if (!PcmciaProcessConfigureRequest(fdoExtension, Socket, &cardRequest)) {
status = STATUS_UNSUCCESSFUL;
DebugPrint((PCMCIA_DEBUG_FAIL, "Failed to configure PcCardIO for socket %x\n", Socket));
}
}
//
// Set up Memory space if there is some.
//
if (NT_SUCCESS(status) && SocketConfig->NumberOfMemoryRanges) {
cardRequest.RequestType = MEM_REQUEST;
cardRequest.u.Memory.NumberOfRanges = (USHORT) SocketConfig->NumberOfMemoryRanges;
for (i = 0; i < SocketConfig->NumberOfMemoryRanges; i++) {
DebugPrint((PCMCIA_DEBUG_CONFIG, "\tmemory: host %08x for 0x%x, card %08x\n",
SocketConfig->Memory[i].HostBase,
SocketConfig->Memory[i].Length,
SocketConfig->Memory[i].CardBase));
cardRequest.u.Memory.MemoryEntry[i].BaseAddress = SocketConfig->Memory[i].CardBase;
cardRequest.u.Memory.MemoryEntry[i].HostAddress = SocketConfig->Memory[i].HostBase;
cardRequest.u.Memory.MemoryEntry[i].WindowSize = SocketConfig->Memory[i].Length;
cardRequest.u.Memory.MemoryEntry[i].AttributeMemory = SocketConfig->Memory[i].IsAttribute;
cardRequest.u.Memory.MemoryEntry[i].WindowDataSize16 = SocketConfig->Memory[i].Width16;
cardRequest.u.Memory.MemoryEntry[i].WaitStates = SocketConfig->Memory[i].WaitState;
}
if (!PcmciaProcessConfigureRequest(fdoExtension, Socket, &cardRequest)) {
status = STATUS_UNSUCCESSFUL;
DebugPrint((PCMCIA_DEBUG_FAIL, "Failed to configure PcCardMem for socket %x\n", Socket));
}
}
return status;
}
NTSTATUS
PcmciaConfigurePcCardIrq(
IN PSOCKET Socket,
IN PSOCKET_CONFIGURATION SocketConfig
)
/*++
Routine Description:
This routine enables the socket IRQ
Arguments:
Socket - Pointer to the socket containing the PC-Card
SocketConfig - Pointer to the socket configuration structure which contains the
resources required to enable this pc-card
Return value:
status
--*/
{
CARD_REQUEST cardRequest = {0};
PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
NTSTATUS status = STATUS_SUCCESS;
DebugPrint((PCMCIA_DEBUG_CONFIG, "skt %08x irq=0x%x\n",
Socket,
SocketConfig->Irq));
//
// Set the IRQ on the controller.
//
if (SocketConfig->Irq) {
cardRequest.RequestType = IRQ_REQUEST;
cardRequest.u.Irq.AssignedIRQ = (UCHAR) SocketConfig->Irq;
cardRequest.u.Irq.ReadyIRQ = (UCHAR) SocketConfig->ReadyIrq;
if (!PcmciaProcessConfigureRequest(fdoExtension, Socket, &cardRequest)) {
status = STATUS_UNSUCCESSFUL;
DebugPrint((PCMCIA_DEBUG_FAIL, "Failed to configure PcCardIrq for socket %x\n", Socket));
}
}
return status;
}
NTSTATUS
PcmciaConfigurePcCardRegisters(
PPDO_EXTENSION pdoExtension
)
/*++
Routine Description:
This routine does the work of configuring the function configuration registers
on the card.
Arguments:
pdoExtension - Pointer to the physical device object extension for the pc-card
Return value:
status
--*/
{
PSOCKET Socket = pdoExtension->Socket;
PSOCKET_CONFIGURATION SocketConfig = pdoExtension->SocketConfiguration;
PSOCKET_DATA socketData ;
CARD_REQUEST cardRequest = {0};
PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
NTSTATUS status = STATUS_UNSUCCESSFUL;
ULONG ccrBase;
PFUNCTION_CONFIGURATION fnConfig;
UCHAR configIndex;
//
// Set up the configuration index on the PCCARD.
//
cardRequest.RequestType = CONFIGURE_REQUEST;
fnConfig = SocketConfig->FunctionConfiguration;
socketData = pdoExtension->SocketData;
ASSERT(socketData != NULL);
do {
cardRequest.u.Config.RegisterWriteMask = 0;
if (fnConfig) {
//
// MF card -
// pick up the base and options from the linked list
//
ccrBase = fnConfig->ConfigRegisterBase;
configIndex = fnConfig->ConfigOptions;
} else {
//
// Single function card -
// get the base and index from base config structure
//
ccrBase = SocketConfig->ConfigRegisterBase;
configIndex = SocketConfig->IndexForCurrentConfiguration;
}
DebugPrint((PCMCIA_DEBUG_CONFIG, "pdo %08x config registers ccr %x\n", pdoExtension->DeviceObject, ccrBase));
//
// We support only 2 interfaces:
// Memory only
// I/o and memory
// We consider a card to be memory only if:
// The card is of device type PCCARD_TYPE_MEMORY: this is true
// for flash memory cards currently
// OR
// The card doesn't have any i/o ranges & the config register base is 0.
//
if (((ccrBase == 0) && (SocketConfig->NumberOfIoPortRanges == 0)) ||
(socketData->DeviceType == PCCARD_TYPE_MEMORY) ||
(socketData->DeviceType == PCCARD_TYPE_FLASH_MEMORY)) {
cardRequest.u.Config.InterfaceType = CONFIG_INTERFACE_MEM;
} else {
//
// i/o mem card
//
cardRequest.u.Config.ConfigBase = ccrBase;
cardRequest.u.Config.InterfaceType = CONFIG_INTERFACE_IO_MEM;
cardRequest.u.Config.RegisterWriteMask |= REGISTER_WRITE_CONFIGURATION_INDEX;
cardRequest.u.Config.ConfigIndex = configIndex;
if (IsConfigRegisterPresent(socketData, 1)) {
cardRequest.u.Config.RegisterWriteMask |= REGISTER_WRITE_CARD_CONFIGURATION;
cardRequest.u.Config.CardConfiguration = 0;
}
if (fnConfig) {
//
// MF card - set up the rest of the configuration registers
//
// Just check audio for now
if (fnConfig->ConfigFlags & 0x8) {
// probably a modem
cardRequest.u.Config.CardConfiguration = 0x08;
}
if (fnConfig->ConfigOptions & 0x02) {
cardRequest.u.Config.IoBaseRegister = fnConfig->IoBase;
cardRequest.u.Config.IoLimitRegister = fnConfig->IoLimit;
cardRequest.u.Config.RegisterWriteMask |= (REGISTER_WRITE_IO_BASE | REGISTER_WRITE_IO_LIMIT);
}
} else if (IsDeviceFlagSet(pdoExtension, PCMCIA_PDO_ENABLE_AUDIO)) {
//
// Request that the audio pin in the card configuration register
// be set.
//
cardRequest.u.Config.CardConfiguration = 0x08;
}
}
if (!PcmciaProcessConfigureRequest(fdoExtension, Socket, &cardRequest)) {
DebugPrint((PCMCIA_DEBUG_FAIL, "Failed to configure PcCardRegisters for PDO %x\n", pdoExtension->DeviceObject));
return status;
}
if (fnConfig) {
fnConfig = fnConfig->Next;
} else {
//
// Remember that the socket is configured and what index was used.
//
socketData->ConfigIndexUsed = configIndex;
}
} while(fnConfig);
status = STATUS_SUCCESS;
return status;
}
VOID
PcmciaConfigureModemHack(
IN PSOCKET Socket,
IN PSOCKET_CONFIGURATION SocketConfig
)
/*++
Routine Description:
This routine does magic to wake the modem up. It is written to accomodate
the Motorola MobileSURFR 56k, but there may be other modems that need it.
Arguments:
Socket - Pointer to the socket containing the PC-Card
SocketConfig - Pointer to the socket configuration structure which contains the
resources required to enable this pc-card
Return value:
status
--*/
{
static const ULONG ValidPortBases[4] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
ULONG i;
UCHAR ch;
ULONG base;
for (i = 0; i < 4; i++) {
base = SocketConfig->Io[0].Base;
if (base == ValidPortBases[i]) {
DebugPrint((PCMCIA_DEBUG_CONFIG, "skt %08x ModemHack base %x\n", Socket, base));
// read and write the modem control register
ch = READ_PORT_UCHAR((PUCHAR)ULongToPtr(base + 4));
WRITE_PORT_UCHAR((PUCHAR)ULongToPtr(base + 4), ch);
break;
}
}
}
VOID
PcmciaSocketDeconfigure(
IN PSOCKET Socket
)
/*++
Routine Description:
deconfigures the card
Arguments:
Socket - Pointer to the socket containing the PC-Card
Return Value
none
--*/
{
CARD_REQUEST cardReq;
if (IsSocketFlagSet(Socket, SOCKET_CARD_CONFIGURED)) {
cardReq.RequestType = DECONFIGURE_REQUEST;
PcmciaProcessConfigureRequest(Socket->DeviceExtension, Socket, &cardReq);
ResetSocketFlag(Socket, SOCKET_CARD_CONFIGURED);
}
//
// 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(Socket->DeviceExtension->Pdo, BusRelations);
}
}
BOOLEAN
PcmciaProcessConfigureRequest(
IN PFDO_EXTENSION DeviceExtension,
IN PSOCKET Socket,
IN PCARD_REQUEST CardConfigurationRequest
)
/*++
Routine Description:
Actually configures the card
Arguments:
Context
Return Value
True
--*/
{
BOOLEAN status;
ULONG counter;
PCMCIA_ACQUIRE_DEVICE_LOCK(DeviceExtension);
//
// Configuring the card can be tricky: the user may pop out the card while
// configuration is taking place.
//
counter = 0;
do {
status = (*(Socket->SocketFnPtr->PCBProcessConfigureRequest))(Socket,
CardConfigurationRequest,
Socket->AddressPort);
if (!status) {
if (!(Socket->SocketFnPtr->PCBDetectCardInSocket(Socket))) {
//
// Somebody popped out the card
//
break;
}
}
counter++;
} while (!status && counter < PCMCIA_MAX_CONFIG_TRIES);
PCMCIA_RELEASE_DEVICE_LOCK(DeviceExtension);
return status;
}
BOOLEAN
PcmciaVerifyCardInSocket(
IN PSOCKET Socket
)
/*++
Routine Description:
This routine compares the current known state to the id of the
card in the slot to determine if the state is consistent. That is,
if there is no card in the socket, then we would expect to see no
cards enumerated in the socket data. If there is a card in the socket,
then we would expect to see the socket data match the card.
Arguments
Socket - Point to the socket to verify
Return Value
TRUE if the logical state of the socket matches its physical state
FALSE otherwise
--*/
{
NTSTATUS status;
PDEVICE_OBJECT pdo, nextPdo;
PPDO_EXTENSION pdoExtension;
BOOLEAN verified = FALSE;
try {
if (!IsCardInSocket(Socket)) {
leave;
}
if (IsCardBusCardInSocket(Socket)) {
ULONG pciConfig;
ULONG i;
//
// Cardbus card now in slot, check to see if it matches the
// PdoList state.
//
if (!Socket->PdoList) {
leave;
}
for (pdo = Socket->PdoList; pdo!=NULL; pdo=nextPdo) {
pdoExtension = pdo->DeviceExtension;
nextPdo = pdoExtension->NextPdoInSocket;
if (!IsCardBusCard(pdoExtension)) {
leave;
}
for (i = 0; i < 1000; i++) {
GetPciConfigSpace(pdoExtension, CFGSPACE_VENDOR_ID, &pciConfig, sizeof(pciConfig));
if (pdoExtension->CardBusId == pciConfig) {
break;
}
PcmciaWait(10);
}
if (i > 0) {
DebugPrint((PCMCIA_DEBUG_FAIL, "pdo %08x waited %d usec to verify device id %08x\n",
pdoExtension->DeviceObject, i*10, pdoExtension->CardBusId));
}
if (pdoExtension->CardBusId != pciConfig) {
DebugPrint((PCMCIA_DEBUG_FAIL, "pdo %08x verify device id FAILED: %08x %08x\n",
pdoExtension->DeviceObject, pdoExtension->CardBusId, pciConfig));
leave;
}
}
verified = TRUE;
} else {
//
// R2 card now in slot
//
pdo = Socket->PdoList;
if (pdo) {
pdoExtension = pdo->DeviceExtension;
if (Is16BitCard(pdoExtension)) {
//
// Invalidate the cache to force re-reading the CIS
//
pdoExtension->CisCacheSpace = 0xff;
if ((NT_SUCCESS(PcmciaParseFunctionDataForID(pdoExtension->SocketData)))) {
verified = TRUE;
}
}
}
}
} finally {
if (!verified) {
SetSocketFlag(Socket, SOCKET_CARD_STATUS_CHANGE);
}
DebugPrint((PCMCIA_DEBUG_INFO, "skt %08x - card %s\n", Socket, verified ? "not changed" : "changed!"));
}
return verified;
}