mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
948 lines
28 KiB
948 lines
28 KiB
/*++
|
|
|
|
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;
|
|
}
|
|
|