949 lines
28 KiB
C
949 lines
28 KiB
C
/*++
|
||
|
||
Copyright (c) 1997-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
enum.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the bus enum code for Pcmcia driver
|
||
|
||
Authors:
|
||
|
||
Ravisankar Pudipeddi (ravisp) 10/15/96
|
||
Neil Sandlin (neilsa) 1-Jun-1999
|
||
|
||
Environment:
|
||
|
||
Kernel mode only
|
||
|
||
Notes:
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "pch.h"
|
||
|
||
//
|
||
// Internal References
|
||
//
|
||
|
||
NTSTATUS
|
||
PcmciaEnumerateDevices(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
PcmciaEnumerateCardBusCard(
|
||
IN PSOCKET socket,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
PcmciaEnumerateR2Card(
|
||
IN PSOCKET socket
|
||
);
|
||
|
||
NTSTATUS
|
||
PcmciaCreatePdo(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PSOCKET Socket,
|
||
OUT PDEVICE_OBJECT *PdoPtr
|
||
);
|
||
|
||
VOID
|
||
PcmciaSetPowerFromConfigData(
|
||
IN PSOCKET Socket,
|
||
IN PDEVICE_OBJECT Pdo
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, PcmciaEnumerateDevices)
|
||
#pragma alloc_text(PAGE, PcmciaEnumerateCardBusCard)
|
||
#pragma alloc_text(PAGE, PcmciaCreatePdo)
|
||
#pragma alloc_text(PAGE, PcmciaDeviceRelations)
|
||
#endif
|
||
|
||
NTSTATUS
|
||
PcmciaEnumerateDevices(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This enumerates the pcmcia bus which is represented by Fdo (a pointer to the device object representing
|
||
the pcmcia controller. It creates new PDOs for any new PC-Cards which have been discovered
|
||
since the last enumeration
|
||
|
||
Notes:
|
||
|
||
Because a user can yank a pccard at any time, the enumeration code only updates the current socket
|
||
status after PnP has had a chance to gracefully remove the device. Specifically, if a surprise remove
|
||
happens, then a number of things has to happen:
|
||
- power has to be reset on the socket
|
||
- bridge windows need to be closed
|
||
- if cardbus, PCI.SYS needs to be informed
|
||
The problem is that we can't do these things immediately here. Instead, on a surprise remove, we
|
||
check to see if there is still state that has yet to be cleared. If so, we report the socket as
|
||
empty, and bail out, even if another card has been inserted (or the same card re-inserted). Later,
|
||
the remove code will cause a new enumeration call, at which point we can update the state and honestly
|
||
report the new device.
|
||
|
||
Arguments:
|
||
|
||
Fdo - Pointer to the functional device object for the PCMCIA controller which needs to be enumerated
|
||
|
||
Return value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
||
PPDO_EXTENSION pdoExtension = NULL;
|
||
PDEVICE_OBJECT pdo;
|
||
PSOCKET socket;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
ULONG i;
|
||
PDEVICE_OBJECT nextPdo;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugPrint((PCMCIA_DEBUG_ENUM, "fdo %08x enumerate devices\n", Fdo));
|
||
|
||
for (socket = fdoExtension->SocketList; socket != NULL; socket = socket->NextSocket) {
|
||
|
||
if (!IsSocketFlagSet(socket, SOCKET_CARD_STATUS_CHANGE)) {
|
||
//
|
||
// return previous results
|
||
//
|
||
DebugPrint((PCMCIA_DEBUG_ENUM, "fdo %08x skt %08x No status change\n", Fdo, socket));
|
||
continue;
|
||
}
|
||
|
||
ResetDeviceFlag(fdoExtension, PCMCIA_FDO_DISABLE_AUTO_POWEROFF);
|
||
|
||
//
|
||
// The socket either has a new card, or the card has been
|
||
// removed. Either way, the old pdo list on this socket, if
|
||
// it exists, will now be thrown away.
|
||
//
|
||
for (pdo = socket->PdoList; pdo!=NULL; pdo=nextPdo) {
|
||
pdoExtension = pdo->DeviceExtension;
|
||
nextPdo = pdoExtension->NextPdoInSocket;
|
||
pdoExtension->NextPdoInSocket = NULL;
|
||
|
||
DebugPrint((PCMCIA_DEBUG_INFO, "fdo %08x enumeration marking pdo %08x REMOVED\n", Fdo, pdo));
|
||
MarkDevicePhysicallyRemoved(pdoExtension);
|
||
}
|
||
socket->PdoList = NULL;
|
||
|
||
if (fdoExtension->PciCardBusDeviceContext != NULL) {
|
||
//
|
||
// If we previously had a cardbus card, and that status has changed,
|
||
// let PCI learn about the empty slot. If there is currently a
|
||
// cardbus card in the slot then PCI found out anyway.
|
||
//
|
||
status = (*fdoExtension->PciCardBusInterface.DispatchPnp)(fdoExtension->PciCardBusDeviceContext, Irp);
|
||
}
|
||
|
||
//
|
||
// Check to see if we are waiting on a remove to finish cleaning up the socket. If so,
|
||
// then don't enumerate anything just yet. Let the remove happen, and enumerate later.
|
||
//
|
||
if (socket->Flags & SOCKET_CLEANUP_MASK) {
|
||
socket->Flags |= SOCKET_ENUMERATE_PENDING;
|
||
DebugPrint((PCMCIA_DEBUG_ENUM, "fdo %08x skt %08x Enumeration deferred! Waiting on remove\n", Fdo, socket));
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Update the current socket status
|
||
//
|
||
PcmciaGetSocketStatus(socket);
|
||
|
||
//
|
||
// Now that we are committed to enumerating the card, this can be reset
|
||
//
|
||
ResetSocketFlag(socket, SOCKET_CARD_STATUS_CHANGE);
|
||
|
||
//
|
||
// Cleanup PCI state if we learn that the status has changed
|
||
//
|
||
|
||
if (fdoExtension->PciCardBusDeviceContext != NULL) {
|
||
|
||
//
|
||
// Let pci clean up
|
||
//
|
||
(*fdoExtension->PciCardBusInterface.DeleteCardBus)(fdoExtension->PciCardBusDeviceContext);
|
||
fdoExtension->PciCardBusDeviceContext = NULL;
|
||
|
||
if (IsDeviceFlagSet(fdoExtension, PCMCIA_INT_ROUTE_INTERFACE)) {
|
||
//
|
||
// Here is one might dereference the interface, that is if that
|
||
// ever had any affect, which it doesn't.
|
||
//
|
||
ResetDeviceFlag(fdoExtension, PCMCIA_INT_ROUTE_INTERFACE);
|
||
}
|
||
}
|
||
|
||
|
||
if (!IsCardInSocket(socket)) {
|
||
//
|
||
// This socket is empty, continue
|
||
//
|
||
DebugPrint((PCMCIA_DEBUG_ENUM, "fdo %08x skt %08x Socket is empty\n", Fdo, socket));
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// If this is a cardbus card, check to see if we can run it
|
||
//
|
||
|
||
if (IsCardBusCardInSocket(socket) && IsDeviceFlagSet(fdoExtension, PCMCIA_CARDBUS_NOT_SUPPORTED)) {
|
||
|
||
if (!IsSocketFlagSet(socket, SOCKET_SUPPORT_MESSAGE_SENT)) {
|
||
SetSocketFlag(socket, SOCKET_SUPPORT_MESSAGE_SENT);
|
||
PcmciaReportControllerError(fdoExtension, STATUS_CARDBUS_NOT_SUPPORTED);
|
||
}
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Some cards may be in a funky state, particularly if the machine crashed
|
||
// previously... power is shown to be on, but the device won't respond. The fix is
|
||
// to force power off first.
|
||
//
|
||
if (!IsDeviceFlagSet(fdoExtension, PCMCIA_FDO_ON_DEBUG_PATH)) {
|
||
|
||
//
|
||
// Fool SetSocketPower into actually powering off the card no matter what
|
||
// state it thinks it is in
|
||
//
|
||
SetSocketFlag(socket, SOCKET_CARD_POWERED_UP);
|
||
|
||
PcmciaSetSocketPower(socket, NULL, NULL, PCMCIA_POWEROFF);
|
||
}
|
||
|
||
//
|
||
// Power up the socket so the device(s) can be enumerated
|
||
//
|
||
if (!NT_SUCCESS(PcmciaSetSocketPower(socket, NULL, NULL, PCMCIA_POWERON))) {
|
||
//
|
||
// Tell the user there was an error
|
||
//
|
||
PcmciaLogError(fdoExtension, PCMCIA_DEVICE_POWER_ERROR, 1, 0);
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Card found here - but no Pdo for it
|
||
// We create a new pdo for the card & initialize the
|
||
// socket to point to it.
|
||
//
|
||
|
||
if (IsCardBusCardInSocket(socket)) {
|
||
//
|
||
// 32-bit cardbus card. Enum via PCI
|
||
//
|
||
|
||
status = PcmciaEnumerateCardBusCard(socket, Irp);
|
||
|
||
} else {
|
||
//
|
||
// R2 card
|
||
//
|
||
status = PcmciaEnumerateR2Card(socket);
|
||
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
DebugPrint((PCMCIA_DEBUG_FAIL, "fdo %08x CardBus enumeration failed: %x\n", Fdo, status));
|
||
//
|
||
// Tell the user there was an error
|
||
//
|
||
PcmciaSetSocketPower(socket, NULL, NULL, PCMCIA_POWEROFF);
|
||
PcmciaLogError(fdoExtension, PCMCIA_DEVICE_ENUMERATION_ERROR, 1, 0);
|
||
continue;
|
||
}
|
||
|
||
PcmciaSetPowerFromConfigData(socket, fdoExtension->PdoList);
|
||
}
|
||
|
||
fdoExtension->LivePdoCount = 0;
|
||
for (pdo = fdoExtension->PdoList; pdo != NULL; pdo = pdoExtension->NextPdoInFdoChain) {
|
||
pdoExtension = pdo->DeviceExtension;
|
||
if (!IsDevicePhysicallyRemoved(pdoExtension)) {
|
||
fdoExtension->LivePdoCount++;
|
||
}
|
||
}
|
||
|
||
DebugPrint((PCMCIA_DEBUG_ENUM, "fdo %08x live pdo count = %d\n", Fdo, fdoExtension->LivePdoCount));
|
||
|
||
if (fdoExtension->LivePdoCount == 0) {
|
||
//
|
||
// Hint for the controller to check if it should turn itself off
|
||
//
|
||
PcmciaFdoCheckForIdle(fdoExtension);
|
||
}
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
PcmciaSetPowerFromConfigData(
|
||
IN PSOCKET Socket,
|
||
IN PDEVICE_OBJECT Pdo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will reset the socket power if the CIS of the device contains
|
||
more specific power requirements than is shown on the controller hardware.
|
||
|
||
Arguments
|
||
|
||
|
||
Return value
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PPDO_EXTENSION pdoExtension;
|
||
PSOCKET_DATA socketData;
|
||
BOOLEAN powerChange = FALSE;
|
||
|
||
pdoExtension = Pdo->DeviceExtension;
|
||
socketData = pdoExtension->SocketData;
|
||
|
||
//
|
||
// Only change Vcc if we are lowering it
|
||
//
|
||
if (socketData->Vcc && (socketData->Vcc < Socket->Vcc)) {
|
||
//
|
||
// Here we should check the controller if it can support the requested
|
||
// voltage. This isn't implemented, so just hard-wire a check for 5v
|
||
// and 3.3v.
|
||
//
|
||
if ((socketData->Vcc == 50) || (socketData->Vcc == 33)) {
|
||
powerChange = TRUE;
|
||
Socket->Vcc = socketData->Vcc;
|
||
}
|
||
}
|
||
|
||
if (socketData->Vpp1 && (socketData->Vpp1 != Socket->Vpp1)) {
|
||
powerChange = TRUE;
|
||
Socket->Vpp1 = socketData->Vpp1;
|
||
}
|
||
|
||
if (socketData->Vpp2 && (socketData->Vpp2 != Socket->Vpp2)) {
|
||
powerChange = TRUE;
|
||
Socket->Vpp2 = socketData->Vpp2;
|
||
}
|
||
|
||
if (powerChange) {
|
||
PcmciaSetSocketPower(Socket, NULL, NULL, PCMCIA_POWEROFF);
|
||
PcmciaSetSocketPower(Socket, NULL, NULL, PCMCIA_POWERON);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
PcmciaEnumerateCardBusCard(
|
||
IN PSOCKET socket,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This enumerates the cardbus card present in the given cardbus controller
|
||
|
||
Note: this routine effectively parties on Irp->IoStatus.Information. This
|
||
value should not be relied upon after return from this routine.
|
||
|
||
Arguments
|
||
|
||
socket - pointer to the socket structure which contains the cardbus card
|
||
Irp - Enumeration irp (IRP_MN_DEVICE_RELATIONS) sent to the controller
|
||
|
||
Return value
|
||
|
||
Status
|
||
--*/
|
||
{
|
||
PFDO_EXTENSION FdoExtension = socket->DeviceExtension;
|
||
PDEVICE_OBJECT Fdo = FdoExtension->DeviceObject;
|
||
PDEVICE_OBJECT pdo;
|
||
PPDO_EXTENSION pdoExtension = NULL;
|
||
PVOID deviceContext;
|
||
NTSTATUS status;
|
||
ULONG i;
|
||
PDEVICE_RELATIONS deviceRelations;
|
||
|
||
PAGED_CODE();
|
||
//
|
||
// We should have already done a delete on any previous context
|
||
//
|
||
ASSERT(FdoExtension->PciCardBusDeviceContext == NULL);
|
||
ASSERT(!IsDeviceFlagSet(FdoExtension, PCMCIA_INT_ROUTE_INTERFACE));
|
||
|
||
//
|
||
// Call PCI's private AddDevice routine to
|
||
// indicate it needs to play a role in enumerating
|
||
// cardbus cards
|
||
//
|
||
|
||
status = (*FdoExtension->PciCardBusInterface.AddCardBus)(FdoExtension->Pdo, &deviceContext);
|
||
FdoExtension->PciCardBusDeviceContext = deviceContext;
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
status = PcmciaGetInterface(FdoExtension->Pdo,
|
||
&GUID_INT_ROUTE_INTERFACE_STANDARD,
|
||
sizeof(INT_ROUTE_INTERFACE_STANDARD),
|
||
(PINTERFACE) &FdoExtension->PciIntRouteInterface
|
||
);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
SetDeviceFlag(FdoExtension, PCMCIA_INT_ROUTE_INTERFACE);
|
||
}
|
||
|
||
//
|
||
// Call PCI repeatedly till the card is enumerated.
|
||
//
|
||
// We don't let PCI report what has already been reported
|
||
//
|
||
Irp->IoStatus.Information = 0;
|
||
status = STATUS_DEVICE_NOT_READY;
|
||
|
||
for (i = 0; i < CARDBUS_CONFIG_RETRY_COUNT; i++) {
|
||
|
||
status = (*FdoExtension->PciCardBusInterface.DispatchPnp)(deviceContext, Irp);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
//
|
||
// PCI failed this IRP for some reason.
|
||
//
|
||
break;
|
||
}
|
||
deviceRelations = (PDEVICE_RELATIONS) Irp->IoStatus.Information;
|
||
if ((deviceRelations == NULL) ||
|
||
((deviceRelations)->Count <= 0)) {
|
||
//
|
||
// This is the problem case: try again
|
||
//
|
||
DebugPrint((PCMCIA_DEBUG_ENUM, "fdo %08x Pci enumerated ZERO device objects\n", FdoExtension->DeviceObject));
|
||
status = STATUS_DEVICE_NOT_READY;
|
||
} else {
|
||
//
|
||
// Cardbus card is enumerated, get out of this loop
|
||
//
|
||
DebugPrint((PCMCIA_DEBUG_ENUM, "fdo %08x Pci enumerated %d device object(s)\n",
|
||
FdoExtension->DeviceObject, (deviceRelations)->Count));
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
//
|
||
// Let pci clean up
|
||
//
|
||
(*FdoExtension->PciCardBusInterface.DeleteCardBus)(FdoExtension->PciCardBusDeviceContext);
|
||
FdoExtension->PciCardBusDeviceContext = NULL;
|
||
ResetDeviceFlag(FdoExtension, PCMCIA_INT_ROUTE_INTERFACE);
|
||
return status;
|
||
}
|
||
|
||
ASSERT (deviceRelations && (deviceRelations->Count > 0));
|
||
for (i = 0; i < deviceRelations->Count; i++) {
|
||
PDEVICE_OBJECT pdo;
|
||
PPDO_EXTENSION pdoExtension;
|
||
//
|
||
// Create a filter device for this pci owned pdo
|
||
//
|
||
status = PcmciaCreatePdo(Fdo, socket, &pdo);
|
||
if (!NT_SUCCESS(status)) {
|
||
//
|
||
// Cleanup allocated socket data structures, if any
|
||
//
|
||
DebugPrint((PCMCIA_DEBUG_FAIL, "fdo %08x create pdo failed %08x\n", Fdo, status));
|
||
continue;
|
||
}
|
||
DebugPrint((PCMCIA_DEBUG_INFO, "fdo %08x created pdo %08x (cardbus)\n", Fdo, pdo));
|
||
pdoExtension = pdo->DeviceExtension;
|
||
//
|
||
// Layer ourselves on top of the PCI ejected pdo
|
||
//
|
||
pdoExtension->PciPdo = deviceRelations->Objects[i];
|
||
MarkDeviceCardBus(pdoExtension);
|
||
|
||
if (!NT_SUCCESS(PcmciaGetInterface(pdoExtension->PciPdo,
|
||
&GUID_BUS_INTERFACE_STANDARD,
|
||
sizeof(BUS_INTERFACE_STANDARD),
|
||
(PINTERFACE) &pdoExtension->PciBusInterface))) {
|
||
ASSERT(FALSE);
|
||
}
|
||
|
||
GetPciConfigSpace(pdoExtension,
|
||
CFGSPACE_VENDOR_ID,
|
||
&pdoExtension->CardBusId,
|
||
sizeof(ULONG));
|
||
DebugPrint((PCMCIA_DEBUG_ENUM, "pdo %08x CardBusId %08x\n", pdo, pdoExtension->CardBusId));
|
||
|
||
//
|
||
// Make the intline register of this cardbus card match the parent
|
||
//
|
||
PcmciaUpdateInterruptLine(pdoExtension, FdoExtension);
|
||
|
||
//
|
||
// See if there is any CIS data we may need
|
||
//
|
||
PcmciaGetConfigData(pdoExtension);
|
||
//
|
||
// Attach to stack
|
||
//
|
||
pdoExtension->LowerDevice = IoAttachDeviceToDeviceStack(pdo, deviceRelations->Objects[i]);
|
||
//
|
||
// Link this to the flat chain of PDOs hanging off the controller's extension
|
||
//
|
||
pdoExtension->NextPdoInFdoChain = FdoExtension->PdoList;
|
||
FdoExtension->PdoList = pdo;
|
||
pdoExtension->NextPdoInSocket = socket->PdoList;
|
||
socket->PdoList = pdo;
|
||
pdo->Flags &= ~DO_DEVICE_INITIALIZING;
|
||
}
|
||
//
|
||
// Make the current socket point to the head of the pdo list off this socket
|
||
//
|
||
socket->NumberOfFunctions = (UCHAR) deviceRelations->Count;
|
||
if (socket->NumberOfFunctions > 1) {
|
||
//
|
||
// This socket has a multifunction card in it
|
||
//
|
||
SetSocketFlag(socket, SOCKET_CARD_MULTIFUNCTION);
|
||
}
|
||
|
||
FdoExtension->PciAddCardBusCount = deviceRelations->Count;
|
||
SetSocketFlag(socket, SOCKET_CLEANUP_PENDING);
|
||
|
||
ExFreePool(deviceRelations);
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
PcmciaEnumerateR2Card(
|
||
IN PSOCKET socket
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This enumerates the R2 card present in the given PCMCIA controller,
|
||
and updates the internal structures to reflect the new card state.
|
||
|
||
|
||
Arguments
|
||
|
||
socket - pointer to the socket structure which contains the R2 card
|
||
|
||
Return value
|
||
|
||
Status
|
||
--*/
|
||
{
|
||
PFDO_EXTENSION fdoExtension = socket->DeviceExtension;
|
||
PDEVICE_OBJECT Fdo = fdoExtension->DeviceObject;
|
||
PDEVICE_OBJECT pdo;
|
||
PPDO_EXTENSION pdoExtension = NULL;
|
||
UCHAR DeviceType;
|
||
NTSTATUS status;
|
||
|
||
status = PcmciaCreatePdo(Fdo, socket, &pdo);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
DebugPrint((PCMCIA_DEBUG_ENUM, "fdo %08x created PDO %08x (R2)\n", Fdo, pdo));
|
||
//
|
||
// initialize the pointers
|
||
//
|
||
pdoExtension = pdo->DeviceExtension;
|
||
//
|
||
// Add a reference count on the socket for power
|
||
//
|
||
PcmciaRequestSocketPower(pdoExtension, NULL);
|
||
|
||
//
|
||
// Get configuration info
|
||
//
|
||
status = PcmciaGetConfigData(pdoExtension);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
DebugPrint((PCMCIA_DEBUG_FAIL, "socket %08x GetConfigData failed %08x\n", socket, status));
|
||
MarkDeviceDeleted(pdoExtension);
|
||
IoDeleteDevice(pdo);
|
||
return status;
|
||
}
|
||
|
||
DebugPrint((PCMCIA_DEBUG_ENUM, "pdo %08x R2 CardId %x-%x-%x\n", pdo,
|
||
pdoExtension->SocketData->ManufacturerCode,
|
||
pdoExtension->SocketData->ManufacturerInfo,
|
||
pdoExtension->SocketData->CisCrc
|
||
));
|
||
|
||
//
|
||
// Make the socket point to the head of the pdo's hanging off this socket
|
||
//
|
||
socket->PdoList = pdo;
|
||
//
|
||
// Link this to the flat chain of PDOs hanging off the controller's extension
|
||
//
|
||
pdoExtension->NextPdoInFdoChain = fdoExtension->PdoList;
|
||
fdoExtension->PdoList = pdo;
|
||
//
|
||
// Remember if this is a multifunction card in the
|
||
// parent pdo itself
|
||
//
|
||
if (socket->NumberOfFunctions > 1) {
|
||
//
|
||
// This is a multifunction card
|
||
//
|
||
MarkDeviceMultifunction(pdoExtension);
|
||
}
|
||
|
||
pdo->Flags &= ~DO_DEVICE_INITIALIZING;
|
||
|
||
ASSERT(pdoExtension->SocketData);
|
||
DeviceType = pdoExtension->SocketData->DeviceType;
|
||
if (DeviceType == PCCARD_TYPE_MODEM ||
|
||
DeviceType == PCCARD_TYPE_SERIAL ||
|
||
DeviceType == PCCARD_TYPE_NETWORK ||
|
||
DeviceType == PCCARD_TYPE_MULTIFUNCTION3) {
|
||
//
|
||
// We want power IRPs at < DPC_LEVEL
|
||
//
|
||
pdo->Flags |= DO_POWER_PAGABLE;
|
||
}
|
||
|
||
SetSocketFlag(socket, SOCKET_CLEANUP_PENDING);
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PcmciaDeviceRelations(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PIRP Irp,
|
||
IN DEVICE_RELATION_TYPE RelationType,
|
||
OUT PDEVICE_RELATIONS *DeviceRelations
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will force enumeration of the PCMCIA controller represented by Fdo,
|
||
allocate a device relations structure and fill in the count and object array with
|
||
referenced object pointers to the valid PDOs which are created during enumeration
|
||
|
||
Arguments:
|
||
|
||
Fdo - a pointer to the functional device object being enumerated
|
||
Irp - pointer to the Irp
|
||
RelationType - Type of relationship to be retrieved
|
||
DeviceRelations - Structure to store the device relations
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PDEVICE_OBJECT currentPdo;
|
||
PPDO_EXTENSION currentPdoExtension;
|
||
PFDO_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
||
ULONG newRelationsSize, oldRelationsSize = 0;
|
||
PDEVICE_RELATIONS deviceRelations = NULL, oldDeviceRelations;
|
||
ULONG i;
|
||
ULONG count;
|
||
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Handle only bus, ejection & removal relations for now
|
||
//
|
||
|
||
if (RelationType != BusRelations &&
|
||
RelationType != RemovalRelations) {
|
||
DebugPrint((PCMCIA_DEBUG_INFO,
|
||
"PcmciaDeviceRelations: RelationType %d, not handled\n",
|
||
(USHORT) RelationType));
|
||
return STATUS_NOT_SUPPORTED;
|
||
}
|
||
|
||
//
|
||
// Need reenumeration only if bus relations are required
|
||
// We need to save the pointer to the old device relations
|
||
// before we call PcmciaReenumerateDevices, as it might trample
|
||
// on it
|
||
//
|
||
oldDeviceRelations = (PDEVICE_RELATIONS) Irp->IoStatus.Information;
|
||
|
||
// I don't understand how this can be non-null, so I added this
|
||
// assert to find out.
|
||
ASSERT(oldDeviceRelations == NULL);
|
||
|
||
if (RelationType == BusRelations) {
|
||
status = PcmciaEnumerateDevices(Fdo, Irp);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
}
|
||
|
||
if ((fdoExtension->LivePdoCount == 0) ||
|
||
(RelationType == RemovalRelations)) {
|
||
//
|
||
// No PDO's to report, we can return early.
|
||
// If no device_relations structure has yet been allocated, however,
|
||
// we need to allocate one & set the count to zero. This will ensure
|
||
// that regardless of whether we pass this IRP down or not, the IO
|
||
// subsystem won't barf.
|
||
//
|
||
if (oldDeviceRelations == NULL) {
|
||
*DeviceRelations = ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS));
|
||
if (*DeviceRelations == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
(*DeviceRelations)->Count = 0;
|
||
(*DeviceRelations)->Objects[0] = NULL;
|
||
}
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if (!(oldDeviceRelations) || (oldDeviceRelations->Count == 0)) {
|
||
newRelationsSize = sizeof(DEVICE_RELATIONS)+(fdoExtension->LivePdoCount - 1)
|
||
* sizeof(PDEVICE_OBJECT);
|
||
} else {
|
||
oldRelationsSize = sizeof(DEVICE_RELATIONS) +
|
||
(oldDeviceRelations->Count-1) * sizeof(PDEVICE_OBJECT);
|
||
newRelationsSize = oldRelationsSize + fdoExtension->LivePdoCount
|
||
* sizeof(PDEVICE_OBJECT);
|
||
}
|
||
|
||
deviceRelations = ExAllocatePool(PagedPool, newRelationsSize);
|
||
|
||
if (deviceRelations == NULL) {
|
||
|
||
DebugPrint((PCMCIA_DEBUG_FAIL,
|
||
"PcmciaDeviceRelations: unable to allocate %d bytes for device relations\n",
|
||
newRelationsSize));
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (oldDeviceRelations) {
|
||
if ((oldDeviceRelations)->Count > 0) {
|
||
RtlCopyMemory(deviceRelations, oldDeviceRelations, oldRelationsSize);
|
||
}
|
||
count = oldDeviceRelations->Count; // May be zero
|
||
ExFreePool (oldDeviceRelations);
|
||
} else {
|
||
count = 0;
|
||
}
|
||
//
|
||
// Copy the object pointers into the structure
|
||
//
|
||
for (currentPdo = fdoExtension->PdoList ;currentPdo != NULL;
|
||
currentPdo = currentPdoExtension->NextPdoInFdoChain) {
|
||
|
||
currentPdoExtension = currentPdo->DeviceExtension;
|
||
|
||
if (!IsDevicePhysicallyRemoved(currentPdoExtension)) {
|
||
//
|
||
// Return PCI pdo if it's a cardbus card
|
||
//
|
||
if (IsCardBusCard(currentPdoExtension)) {
|
||
ASSERT(currentPdoExtension->PciPdo != NULL);
|
||
//
|
||
// Return the PDO provided by PCI instead of our filter
|
||
//
|
||
deviceRelations->Objects[count++] = currentPdoExtension->PciPdo;
|
||
status = ObReferenceObjectByPointer(currentPdoExtension->PciPdo,
|
||
0,
|
||
NULL,
|
||
KernelMode);
|
||
} else {
|
||
//
|
||
// Devices have to be referenced by the bus driver
|
||
// before returning them to PNP
|
||
//
|
||
deviceRelations->Objects[count++] = currentPdo;
|
||
status = ObReferenceObjectByPointer(currentPdo,
|
||
0,
|
||
NULL,
|
||
KernelMode);
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
DebugPrint((PCMCIA_DEBUG_FAIL, "PcmciaDeviceRelations: status %#08lx "
|
||
"while referencing object %#08lx\n",
|
||
status,
|
||
currentPdo));
|
||
}
|
||
}
|
||
}
|
||
|
||
deviceRelations->Count = count;
|
||
*DeviceRelations = deviceRelations;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
PcmciaCreatePdo(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PSOCKET Socket,
|
||
OUT PDEVICE_OBJECT *PdoPtr
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
Creates and initializes a device object - which will be referred to as a Physical Device
|
||
Object or PDO - for the PC-Card in the socket represented by Socket, hanging off the PCMCIA
|
||
controller represented by Fdo.
|
||
|
||
Arguments:
|
||
|
||
Fdo - Functional device object representing the PCMCIA controller
|
||
Socket - Socket in which the PC-Card for which we're creating a PDO resides
|
||
PdoPtr - Pointer to an area of memory where the created PDO is returned
|
||
|
||
Return value:
|
||
|
||
STATUS_SUCCESS - Pdo creation/initialization successful, PdoPtr contains the pointer
|
||
to the Pdo
|
||
Any other status - creation/initialization unsuccessful
|
||
|
||
--*/
|
||
{
|
||
ULONG pdoNameIndex = 0, socketNumber;
|
||
PSOCKET currentSocket;
|
||
PPDO_EXTENSION pdoExtension;
|
||
PFDO_EXTENSION fdoExtension=Fdo->DeviceExtension;
|
||
char deviceName[128];
|
||
ANSI_STRING ansiName;
|
||
UNICODE_STRING unicodeName;
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Locate socket 'number'
|
||
//
|
||
for (currentSocket=fdoExtension->SocketList, socketNumber=0;
|
||
currentSocket && (currentSocket != Socket);
|
||
currentSocket = currentSocket->NextSocket, socketNumber++);
|
||
|
||
//
|
||
// Allocate space for the Unicode string:(handles upto 0xFFFF
|
||
// devices for now :)
|
||
//
|
||
sprintf(deviceName, "%s%d-%d", PCMCIA_PCCARD_NAME,socketNumber, 0xFFFF);
|
||
RtlInitAnsiString(&ansiName, deviceName);
|
||
status = RtlAnsiStringToUnicodeString(&unicodeName, &ansiName, TRUE);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Attempt to create the device with a unique name
|
||
//
|
||
do {
|
||
sprintf(deviceName, "%s%d-%d", PCMCIA_PCCARD_NAME,socketNumber, pdoNameIndex++);
|
||
RtlInitAnsiString(&ansiName, deviceName);
|
||
status = RtlAnsiStringToUnicodeString(&unicodeName, &ansiName, FALSE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
RtlFreeUnicodeString(&unicodeName);
|
||
return status;
|
||
}
|
||
|
||
status = IoCreateDevice(
|
||
Fdo->DriverObject,
|
||
sizeof(PDO_EXTENSION),
|
||
&unicodeName,
|
||
FILE_DEVICE_UNKNOWN,
|
||
0,
|
||
FALSE,
|
||
PdoPtr
|
||
);
|
||
} while ((status == STATUS_OBJECT_NAME_EXISTS) ||
|
||
(status == STATUS_OBJECT_NAME_COLLISION));
|
||
|
||
RtlFreeUnicodeString(&unicodeName);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Initialize the device extension for the PDO
|
||
//
|
||
pdoExtension = (*PdoPtr)->DeviceExtension;
|
||
RtlZeroMemory(pdoExtension, sizeof(PDO_EXTENSION));
|
||
|
||
pdoExtension->DeviceObject = *PdoPtr;
|
||
pdoExtension->Socket = Socket;
|
||
|
||
//
|
||
// Initialize power states
|
||
//
|
||
pdoExtension->SystemPowerState = PowerSystemWorking;
|
||
pdoExtension->DevicePowerState = PowerDeviceD0;
|
||
|
||
//
|
||
// Initialize pending enable objects
|
||
//
|
||
KeInitializeTimer(&pdoExtension->ConfigurationTimer);
|
||
|
||
KeInitializeDpc(&pdoExtension->ConfigurationDpc,
|
||
PcmciaConfigurationWorker,
|
||
pdoExtension);
|
||
|
||
KeInitializeTimer(&pdoExtension->PowerWorkerTimer);
|
||
|
||
KeInitializeDpc(&pdoExtension->PowerWorkerDpc,
|
||
PcmciaPdoPowerWorkerDpc,
|
||
pdoExtension);
|
||
|
||
//
|
||
// PNP is going to mark the PDO as a DO_BUS_ENUMERATED_DEVICE,
|
||
// but for CardBus cards- the PDO we return is owned by PCI.
|
||
// Hence we need to mark this device object (in that case a
|
||
// filter on top of PCI's PDO) as PDO explicitly.
|
||
//
|
||
MARK_AS_PDO(*PdoPtr);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|