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.
1837 lines
55 KiB
1837 lines
55 KiB
/*++
|
|
|
|
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
|
|
//
|
|
|
|
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) && !Socket->PowerCompletionRoutine) {
|
|
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;
|
|
}
|
|
|