windows-nt/Source/XPSP1/NT/net/homenet/bridge/sys/brdgctl.c
2020-09-26 16:20:57 +08:00

1201 lines
33 KiB
C

/*++
Copyright(c) 1999-2000 Microsoft Corporation
Module Name:
brdgctl.c
Abstract:
Ethernet MAC level bridge.
IOCTL processing code
Author:
Mark Aiken
Environment:
Kernel mode driver
Revision History:
Apr 2000 - Original version
--*/
#define NDIS_MINIPORT_DRIVER
#define NDIS50_MINIPORT 1
#define NDIS_WDM 1
#pragma warning( push, 3 )
#include <ndis.h>
#include <ntddk.h>
#pragma warning( pop )
#include "bridge.h"
#include "brdgmini.h"
#include "brdgtbl.h"
#include "brdgctl.h"
#include "brdgfwd.h"
#include "brdgprot.h"
#include "brdgbuf.h"
#include "brdgsta.h"
// IoSetCancelRoutine causes these warnings; disable them
#pragma warning( disable: 4054 )
#pragma warning( disable: 4055 )
// ===========================================================================
//
// CONSTANTS
//
// ===========================================================================
//
// Maximum number of notifications we will queue up if the user-mode
// code hasn't given us any IRPs to use
//
#define MAX_NOTIFY_QUEUE_LENGTH 20
// ===========================================================================
//
// PRIVATE DECLARATIONS
//
// ===========================================================================
// Structure for queueing a notification
typedef struct _DEFERRED_NOTIFY
{
BSINGLE_LIST_ENTRY List; // For queuing
UINT DataSize; // Size of data at end
BRIDGE_NOTIFY_HEADER Header; // The notification header
// DataSize bytes of data follows
} DEFERRED_NOTIFY, *PDEFERRED_NOTIFY;
// Type of function to pass to BrdgCtlCommonNotify
typedef VOID (*PNOTIFY_COPY_FUNC)(PVOID, PVOID);
// ===========================================================================
//
// GLOBALS
//
// ===========================================================================
// Queue of pending notifications
BSINGLE_LIST_HEAD gNotificationsList;
NDIS_SPIN_LOCK gNotificationsListLock;
// Queue of pending notification IRPs
LIST_ENTRY gIRPList;
NDIS_SPIN_LOCK gIRPListLock;
// A flag controlling whether new entries are allowed onto the queue of pending
// notifications or not. != 0 means new entries are allowed
ULONG gAllowQueuedNotifies = 0L;
// ===========================================================================
//
// LOCAL PROTOTYPES
//
// ===========================================================================
PIRP
BrdgCtlDequeuePendingIRP();
VOID
BrdgCtlCopyAdapterInfo(
OUT PBRIDGE_ADAPTER_INFO pInfo,
IN PADAPT pAdapt
);
NTSTATUS
BrdgCtlQueueAndPendIRP(
IN PIRP pIrp
);
PADAPT
BrdgCtlValidateAcquireAdapter(
IN BRIDGE_ADAPTER_HANDLE Handle
);
VOID
BrdgCtlEmptyIRPList(
PLIST_ENTRY pList
);
VOID
BrdgCtlCancelPendingIRPs();
VOID
BrdgCtlReleaseQueuedNotifications();
// ===========================================================================
//
// PUBLIC FUNCTIONS
//
// ===========================================================================
NTSTATUS
BrdgCtlDriverInit()
/*++
Routine Description:
Main driver entry point. Called at driver load time
Arguments:
None
Return Value:
Status of our initialization. A status != STATUS_SUCCESS aborts the
driver load and we don't get called again.
--*/
{
BrdgInitializeSingleList( &gNotificationsList );
InitializeListHead( &gIRPList );
NdisAllocateSpinLock( &gNotificationsListLock );
NdisAllocateSpinLock( &gIRPListLock );
return STATUS_SUCCESS;
}
VOID
BrdgCtlHandleCreate()
/*++
Routine Description:
Called when a user-mode component opens our device object. We allow notifications
to be queued up until we are closed.
Arguments:
None
Return Value:
None
--*/
{
DBGPRINT(CTL, ("BrdgCtlHandleCreate()\n"));
// Permit notifications to be queued
InterlockedExchangeULong( &gAllowQueuedNotifies, 1L );
}
VOID
BrdgCtlHandleCleanup()
/*++
Routine Description:
Called when our device object has no more references to it. We disallow notification
queuing and flush existing queued notifications and pending IRPs.
Arguments:
None
Return Value:
None
--*/
{
// Forbid new notifications from being queued
ULONG prev = InterlockedExchangeULong( &gAllowQueuedNotifies, 0L );
DBGPRINT(CTL, ("BrdgCtlHandleCleanup()\n"));
// Write in this roundabout way otherwise compiler complains about
// prev not being used in the FRE build
if( prev == 0L )
{
SAFEASSERT( FALSE );
}
// Complete any pending IRPs
BrdgCtlCancelPendingIRPs();
// Ditch any queued notifications
BrdgCtlReleaseQueuedNotifications();
}
VOID
BrdgCtlCommonNotify(
IN PADAPT pAdapt,
IN BRIDGE_NOTIFICATION_TYPE Type,
IN ULONG DataSize,
IN OPTIONAL PNOTIFY_COPY_FUNC pFunc,
IN PVOID Param1
)
/*++
Routine Description:
Common processing for notifications to user-mode
This routine completes a pending IRP from user mode if one is
available. Otherwise, it queues up a new DEFERRED_NOTIFY
structure with the notification data.
Arguments:
pAdapt The adapter involved in the notification
Type Type of notification
DataSize Required amount of data required to store
the notification information
pFunc A function that can copy the notification
data to an IRP's buffer or a new DEFERRED_NOTIFY
structure. Can be NULL if no copying is
required.
Param1 A context pointer to pass to the helper
function
Return Value:
None
--*/
{
PIRP pIrp;
// Check if there is an IRP waiting to receive this notification
pIrp = BrdgCtlDequeuePendingIRP();
if( pIrp != NULL )
{
PBRIDGE_NOTIFY_HEADER pHeader;
// There's an IRP waiting to be completed. Fill it in
pHeader = (PBRIDGE_NOTIFY_HEADER)pIrp->AssociatedIrp.SystemBuffer;
// Fill in the notification header
pHeader->Handle = (BRIDGE_ADAPTER_HANDLE)pAdapt;
pHeader->NotifyType = Type;
// Fill in the remaining data if necessary
if( pFunc != NULL )
{
(*pFunc)( ((PUCHAR)pIrp->AssociatedIrp.SystemBuffer) + sizeof(BRIDGE_NOTIFY_HEADER), Param1 );
}
// Complete the IRP
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = sizeof(BRIDGE_NOTIFY_HEADER) + DataSize;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
}
else
{
// No pending IRP. Queue up the notification if that's currently allowed.
if( gAllowQueuedNotifies )
{
NDIS_STATUS Status;
PDEFERRED_NOTIFY pNewNotify, pOldEntry = NULL;
Status = NdisAllocateMemoryWithTag( &pNewNotify, sizeof(DEFERRED_NOTIFY) + DataSize, 'gdrB' );
if( Status != NDIS_STATUS_SUCCESS )
{
DBGPRINT(CTL, ("Failed to allocate memory for an adapter change notification: %08x\n", Status));
return;
}
// Fill in the notification
pNewNotify->DataSize = DataSize;
pNewNotify->Header.Handle = (BRIDGE_ADAPTER_HANDLE)pAdapt;
pNewNotify->Header.NotifyType = Type;
// Fill the remaining data if necessary
if( pFunc != NULL )
{
(*pFunc)( ((PUCHAR)pNewNotify) + sizeof(DEFERRED_NOTIFY), Param1 );
}
NdisAcquireSpinLock( &gNotificationsListLock );
SAFEASSERT( BrdgQuerySingleListLength(&gNotificationsList) <= MAX_NOTIFY_QUEUE_LENGTH );
// Enforce the maximum notification queue length
if( BrdgQuerySingleListLength(&gNotificationsList) == MAX_NOTIFY_QUEUE_LENGTH )
{
// Dequeue and ditch the head (oldest) notification
pOldEntry = (PDEFERRED_NOTIFY)BrdgRemoveHeadSingleList( &gNotificationsList );
}
// Enqueue our entry
BrdgInsertTailSingleList( &gNotificationsList, &pNewNotify->List );
NdisReleaseSpinLock( &gNotificationsListLock );
if( pOldEntry != NULL )
{
// Release the old entry that we bumped off
NdisFreeMemory( pOldEntry, sizeof(DEFERRED_NOTIFY) + pOldEntry->DataSize, 0 );
}
}
}
}
VOID
BrdgCtlNotifyAdapterChange(
IN PADAPT pAdapt,
IN BRIDGE_NOTIFICATION_TYPE Type
)
/*++
Routine Description:
Produces a notification to user-mode signalling a change in an adapter.
Arguments:
pAdapt The adapter involved
Type Type of notification
Return Value:
None
--*/
{
if( Type == BrdgNotifyRemoveAdapter )
{
// We don't pass any additional data in the notification for remove events
BrdgCtlCommonNotify( pAdapt, Type, 0, NULL, NULL );
}
else
{
BrdgCtlCommonNotify( pAdapt, Type, sizeof(BRIDGE_ADAPTER_INFO), BrdgCtlCopyAdapterInfo, pAdapt );
}
}
NTSTATUS
BrdgCtlHandleIoDeviceControl(
IN PIRP Irp,
IN PFILE_OBJECT FileObject,
IN OUT PVOID Buffer,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength,
IN ULONG IoControlCode,
OUT PULONG Information
)
/*++
Routine Description:
This routine handles all Device-control requests.
Arguments:
Irp The IRP
FileObject The file object of the bridge
Buffer Input / output buffer
InputBufferLength Size of inbound data
OutputBufferLength Maximum allowable output data
IoControlCode The control code
Information Code-specific information returned
(usually the number of written bytes or
bytes required on overflow)
Return Value:
Status of the operation
--*/
{
NTSTATUS status = STATUS_SUCCESS;
*Information = 0;
switch (IoControlCode)
{
//
// Request for notification
//
case BRIDGE_IOCTL_REQUEST_NOTIFY:
{
PDEFERRED_NOTIFY pDeferred = NULL;
if( OutputBufferLength < sizeof(BRIDGE_NOTIFY_HEADER) + MAX_PACKET_SIZE )
{
status = STATUS_BUFFER_TOO_SMALL;
}
else
{
// See if there is a pending notification waiting for an IRP
NdisAcquireSpinLock( &gNotificationsListLock );
if( BrdgQuerySingleListLength(&gNotificationsList) > 0L )
{
PBSINGLE_LIST_ENTRY pList = BrdgRemoveHeadSingleList(&gNotificationsList);
if( pList != NULL )
{
pDeferred = CONTAINING_RECORD( pList, DEFERRED_NOTIFY, List );
}
else
{
// Should be impossible
SAFEASSERT(FALSE);
}
}
NdisReleaseSpinLock( &gNotificationsListLock );
if( pDeferred != NULL )
{
UINT SizeToCopy = sizeof(BRIDGE_NOTIFY_HEADER) + pDeferred->DataSize;
// We have a notification to return immediately
NdisMoveMemory( Buffer, &pDeferred->Header, SizeToCopy );
*Information = SizeToCopy;
// Free the holding structure
NdisFreeMemory( pDeferred, sizeof(DEFERRED_NOTIFY) + pDeferred->DataSize, 0 );
}
else
{
// No pending notification to send. queue the IRP for use later
status = BrdgCtlQueueAndPendIRP( Irp );
}
}
}
break;
//
// Request to be notified about all adapters
//
case BRIDGE_IOCTL_GET_ADAPTERS:
{
// Send a notification for each adapter
PADAPT pAdapt;
LOCK_STATE LockState;
NdisAcquireReadWriteLock( &gAdapterListLock, FALSE/*Read only*/, &LockState );
for( pAdapt = gAdapterList; pAdapt != NULL; pAdapt = pAdapt->Next )
{
BrdgCtlNotifyAdapterChange( pAdapt, BrdgNotifyEnumerateAdapters );
}
NdisReleaseReadWriteLock( &gAdapterListLock, &LockState );
}
break;
//
// Request for an adapter's device name
//
case BRIDGE_IOCTL_GET_ADAPT_DEVICE_NAME:
{
if( InputBufferLength < sizeof(BRIDGE_ADAPTER_HANDLE) )
{
status = STATUS_BUFFER_TOO_SMALL;
}
else
{
PADAPT pAdapt = BrdgCtlValidateAcquireAdapter(*((PBRIDGE_ADAPTER_HANDLE)Buffer));
if( pAdapt == NULL )
{
// The handle passed in doesn't actually indicate an adapter
status = STATUS_INVALID_PARAMETER;
}
else
{
ULONG bytesToCopy;
// We need enough room to add a trailing NULL
if( OutputBufferLength < pAdapt->DeviceName.Length + sizeof(WCHAR) )
{
if( OutputBufferLength >= sizeof(WCHAR) )
{
bytesToCopy = OutputBufferLength - sizeof(WCHAR);
}
else
{
bytesToCopy = 0L;
}
status = STATUS_BUFFER_OVERFLOW;
}
else
{
bytesToCopy = pAdapt->DeviceName.Length;
}
if( bytesToCopy > 0L )
{
NdisMoveMemory( Buffer, pAdapt->DeviceName.Buffer, bytesToCopy );
}
// Put a trailing NULL WCHAR at the end
*((PWCHAR)((PUCHAR)Buffer + bytesToCopy)) = 0x0000;
// Tell the caller how many bytes we wrote / are needed
*Information = pAdapt->DeviceName.Length + sizeof(WCHAR);
BrdgReleaseAdapter(pAdapt);
}
}
}
break;
//
// Request for an adapter's friendly name
//
case BRIDGE_IOCTL_GET_ADAPT_FRIENDLY_NAME:
{
if( InputBufferLength < sizeof(BRIDGE_ADAPTER_HANDLE) )
{
status = STATUS_BUFFER_TOO_SMALL;
}
else
{
PADAPT pAdapt = BrdgCtlValidateAcquireAdapter(*((PBRIDGE_ADAPTER_HANDLE)Buffer));
if( pAdapt == NULL )
{
// The handle passed in doesn't actually indicate an adapter
status = STATUS_INVALID_PARAMETER;
}
else
{
ULONG bytesToCopy;
// We need enough room to add a trailing NULL
if( OutputBufferLength < pAdapt->DeviceDesc.Length + sizeof(WCHAR) )
{
if( OutputBufferLength >= sizeof(WCHAR) )
{
bytesToCopy = OutputBufferLength - sizeof(WCHAR);
}
else
{
bytesToCopy = 0L;
}
status = STATUS_BUFFER_OVERFLOW;
}
else
{
bytesToCopy = pAdapt->DeviceDesc.Length;
}
if( bytesToCopy > 0L )
{
NdisMoveMemory( Buffer, pAdapt->DeviceDesc.Buffer, bytesToCopy );
}
// Put a trailing NULL WCHAR at the end
*((PWCHAR)((PUCHAR)Buffer + bytesToCopy)) = 0x0000;
// Tell the caller how many bytes we wrote / are needed
*Information = pAdapt->DeviceDesc.Length + sizeof(WCHAR);
BrdgReleaseAdapter(pAdapt);
}
}
}
break;
//
// Request to retrieve the bridge's MAC address
//
case BRIDGE_IOCTL_GET_MAC_ADDRESS:
{
if( OutputBufferLength < ETH_LENGTH_OF_ADDRESS )
{
status = STATUS_BUFFER_TOO_SMALL;
}
else
{
if( ! BrdgMiniReadMACAddress((PUCHAR)Buffer) )
{
// We don't actually have a MAC address right now
// (shouldn't really be possible since the user-mode code would have
// to be making this request before we bound to any adapters)
status = STATUS_UNSUCCESSFUL;
}
else
{
*Information = ETH_LENGTH_OF_ADDRESS;
}
}
}
break;
//
// Request to retrieve packet-handling statistics
//
case BRIDGE_IOCTL_GET_PACKET_STATS:
{
if( OutputBufferLength < sizeof(BRIDGE_PACKET_STATISTICS) )
{
status = STATUS_BUFFER_TOO_SMALL;
}
else
{
PBRIDGE_PACKET_STATISTICS pStats = (PBRIDGE_PACKET_STATISTICS)Buffer;
// These are only statistics and have no associated locks so just read them
// without protection
pStats->TransmittedFrames = gStatTransmittedFrames;
pStats->TransmittedErrorFrames = gStatTransmittedErrorFrames;
pStats->TransmittedBytes = gStatTransmittedBytes;
pStats->DirectedTransmittedFrames = gStatDirectedTransmittedFrames;
pStats->MulticastTransmittedFrames = gStatMulticastTransmittedFrames;
pStats->BroadcastTransmittedFrames = gStatBroadcastTransmittedFrames;
pStats->DirectedTransmittedBytes = gStatDirectedTransmittedBytes;
pStats->MulticastTransmittedBytes = gStatMulticastTransmittedBytes;
pStats->BroadcastTransmittedBytes = gStatBroadcastTransmittedBytes;
pStats->IndicatedFrames = gStatIndicatedFrames;
pStats->IndicatedDroppedFrames = gStatIndicatedDroppedFrames;
pStats->IndicatedBytes = gStatIndicatedBytes;
pStats->DirectedIndicatedFrames = gStatDirectedIndicatedFrames;
pStats->MulticastIndicatedFrames = gStatMulticastIndicatedFrames;
pStats->BroadcastIndicatedFrames = gStatBroadcastIndicatedFrames;
pStats->DirectedIndicatedBytes = gStatDirectedIndicatedBytes;
pStats->MulticastIndicatedBytes = gStatMulticastIndicatedBytes;
pStats->BroadcastIndicatedBytes = gStatBroadcastIndicatedBytes;
pStats->ReceivedFrames = gStatReceivedFrames;
pStats->ReceivedBytes = gStatReceivedBytes;
pStats->ReceivedCopyFrames = gStatReceivedCopyFrames;
pStats->ReceivedCopyBytes = gStatReceivedCopyBytes;
pStats->ReceivedNoCopyFrames = gStatReceivedNoCopyFrames;
pStats->ReceivedNoCopyBytes = gStatReceivedNoCopyBytes;
*Information = sizeof(BRIDGE_PACKET_STATISTICS);
}
}
break;
//
// Request to retrieve packet-handling statistics for an adapter
//
case BRIDGE_IOCTL_GET_ADAPTER_PACKET_STATS:
{
if( (InputBufferLength < sizeof(BRIDGE_ADAPTER_HANDLE)) ||
(OutputBufferLength < sizeof(BRIDGE_ADAPTER_PACKET_STATISTICS)) )
{
status = STATUS_BUFFER_TOO_SMALL;
}
else
{
PADAPT pAdapt = BrdgCtlValidateAcquireAdapter(*((PBRIDGE_ADAPTER_HANDLE)Buffer));
if( pAdapt == NULL )
{
// The handle passed in doesn't actually indicate an adapter
status = STATUS_INVALID_PARAMETER;
}
else
{
PBRIDGE_ADAPTER_PACKET_STATISTICS pStats = (PBRIDGE_ADAPTER_PACKET_STATISTICS)Buffer;
// These are only statistics and have no associated locks so just read them
// without protection
pStats->SentFrames = pAdapt->SentFrames;
pStats->SentBytes = pAdapt->SentBytes;
pStats->SentLocalFrames = pAdapt->SentLocalFrames;
pStats->SentLocalBytes = pAdapt->SentLocalBytes;
pStats->ReceivedFrames = pAdapt->ReceivedFrames;
pStats->ReceivedBytes = pAdapt->ReceivedBytes;
*Information = sizeof(BRIDGE_ADAPTER_PACKET_STATISTICS);
BrdgReleaseAdapter(pAdapt);
}
}
}
break;
//
// Request to retrieve buffer-handling statistics
//
case BRIDGE_IOCTL_GET_BUFFER_STATS:
{
if( OutputBufferLength < sizeof(BRIDGE_BUFFER_STATISTICS) )
{
status = STATUS_BUFFER_TOO_SMALL;
}
else
{
BrdgBufGetStatistics((PBRIDGE_BUFFER_STATISTICS)Buffer);
*Information = sizeof(BRIDGE_BUFFER_STATISTICS);
}
}
break;
//
// Request to alter the packet-retention policy
//
case BRIDGE_IOCTL_RETAIN_PACKETS:
case BRIDGE_IOCTL_NO_RETAIN_PACKETS:
{
// This global flag is not protected by any lock
gRetainNICPackets = (BOOLEAN)(IoControlCode == BRIDGE_IOCTL_RETAIN_PACKETS);
}
break;
//
// Request to retrieve the contents of the forwarding table for
// a particular adapter
//
case BRIDGE_IOCTL_GET_TABLE_ENTRIES:
{
if( InputBufferLength < sizeof(BRIDGE_ADAPTER_HANDLE) )
{
status = STATUS_BUFFER_TOO_SMALL;
}
else
{
PADAPT pAdapt = BrdgCtlValidateAcquireAdapter(*((PBRIDGE_ADAPTER_HANDLE)Buffer));
if( pAdapt == NULL )
{
// The handle passed in doesn't actually indicate an adapter
status = STATUS_INVALID_PARAMETER;
}
else
{
ULONG ReqdBytes;
// Try to read the contents of the forwarding table for this adapter
ReqdBytes = BrdgTblReadTable( pAdapt, Buffer, OutputBufferLength );
if( ReqdBytes > OutputBufferLength )
{
status = STATUS_BUFFER_OVERFLOW;
}
*Information = ReqdBytes;
BrdgReleaseAdapter(pAdapt);
}
}
}
break;
case BRIDGE_IOCTL_GET_ADAPTER_STA_INFO:
{
if( gDisableSTA )
{
// Can't collect STA information when it's not running!
status = STATUS_INVALID_PARAMETER;
}
else if( InputBufferLength < sizeof(BRIDGE_ADAPTER_HANDLE) ||
OutputBufferLength < sizeof(BRIDGE_STA_ADAPTER_INFO) )
{
status = STATUS_BUFFER_TOO_SMALL;
}
else
{
PADAPT pAdapt = BrdgCtlValidateAcquireAdapter(*((PBRIDGE_ADAPTER_HANDLE)Buffer));
if( pAdapt == NULL )
{
// The handle passed in doesn't actually indicate an adapter
status = STATUS_INVALID_PARAMETER;
}
else
{
BrdgSTAGetAdapterSTAInfo( pAdapt, (PBRIDGE_STA_ADAPTER_INFO)Buffer );
*Information = sizeof(BRIDGE_STA_ADAPTER_INFO);
BrdgReleaseAdapter(pAdapt);
}
}
}
break;
case BRIDGE_IOCTL_GET_GLOBAL_STA_INFO:
{
if( gDisableSTA )
{
// Can't collect STA information when it's not running!
status = STATUS_INVALID_PARAMETER;
}
else if( OutputBufferLength < sizeof(BRIDGE_STA_GLOBAL_INFO) )
{
status = STATUS_BUFFER_TOO_SMALL;
}
else
{
BrdgSTAGetSTAInfo( (PBRIDGE_STA_GLOBAL_INFO)Buffer );
*Information = sizeof(BRIDGE_STA_GLOBAL_INFO);
}
}
break;
default:
{
status = STATUS_INVALID_PARAMETER;
}
break;
}
return status;
}
// ===========================================================================
//
// PRIVATE FUNCTIONS
//
// ===========================================================================
VOID
BrdgCtlCopyAdapterInfo(
OUT PBRIDGE_ADAPTER_INFO pInfo,
IN PADAPT pAdapt
)
/*++
Routine Description:
Helper function for BrdgCtlCommonNotify. Copies data about an adapter
to a buffer.
Arguments:
pInfo Structure to fill with information
pAdapt Adapter to copy from
Return Value:
None
--*/
{
LOCK_STATE LockState;
// Take a read lock on gAdapterCharacteristicsLock to ensure that all these
// are consistent
NdisAcquireReadWriteLock( &gAdapterCharacteristicsLock, FALSE/*Read only*/, &LockState );
pInfo->LinkSpeed = pAdapt->LinkSpeed;
pInfo->MediaState = pAdapt->MediaState;
pInfo->State = pAdapt->State;
NdisReleaseReadWriteLock( &gAdapterCharacteristicsLock, &LockState );
// These values don't change after assignment, and so need no lock.
ETH_COPY_NETWORK_ADDRESS( pInfo->MACAddress, pAdapt->MACAddr );
pInfo->PhysicalMedium = pAdapt->PhysicalMedium;
}
PADAPT
BrdgCtlValidateAcquireAdapter(
IN BRIDGE_ADAPTER_HANDLE Handle
)
/*++
Routine Description:
Checks to ensure that a BRIDGE_ADAPTER_HANDLE passed from user-mode code
actually corresponds to an adapter still in our list.
If the adapter is found, its refcount is incremented.
Arguments:
Handle A handle from user-mode code
Return Value:
The handle recast as a PADAPT, or NULL if the adapter could not be found.
--*/
{
PADAPT pAdapt = (PADAPT)Handle, anAdapt;
LOCK_STATE LockState;
NdisAcquireReadWriteLock( &gAdapterListLock, FALSE/*Read only*/, &LockState );
for( anAdapt = gAdapterList; anAdapt != NULL; anAdapt = anAdapt->Next )
{
if( anAdapt == pAdapt )
{
// The adapter is in the list. Increment its refcount inside the lock
// and return
BrdgAcquireAdapterInLock( pAdapt );
NdisReleaseReadWriteLock( &gAdapterListLock, &LockState );
return pAdapt;
}
}
NdisReleaseReadWriteLock( &gAdapterListLock, &LockState );
return NULL;
}
VOID
BrdgCtlCancelIoctl(
PDEVICE_OBJECT DeviceObject,
PIRP pIrp
)
/*++
Routine Description:
IRP Cancellation function
Arguments:
DeviceObject The bridge's device-object
pIrp The IRP to be cancelled
Return Value:
none.
Environment:
Invoked with the cancel spin-lock held by the I/O manager.
It is this routine's responsibility to release the lock.
--*/
{
IoReleaseCancelSpinLock(pIrp->CancelIrql);
// Take the IRP off our list
NdisAcquireSpinLock( &gIRPListLock );
RemoveEntryList( &pIrp->Tail.Overlay.ListEntry );
InitializeListHead( &pIrp->Tail.Overlay.ListEntry );
NdisReleaseSpinLock( &gIRPListLock );
// Complete the IRP
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
}
NTSTATUS
BrdgCtlQueueAndPendIRP(
IN PIRP pIrp
)
/*++
Routine Description:
Safely inserts an IRP into our queue of pending IRPs.
Arguments:
pIrp The IRP to queue
Return Value:
The status to return from IRP processing (can be STATUS_CANCELLED
if the IRP was cancelled right after we received it. Otherwise
is STATUS_PENDING so caller knows the IRP is pending).
--*/
{
KIRQL CancelIrql;
// If the IRP has already been cancelled, forget it.
IoAcquireCancelSpinLock( &CancelIrql );
NdisDprAcquireSpinLock( &gIRPListLock );
if ( pIrp->Cancel )
{
NdisDprReleaseSpinLock( &gIRPListLock );
IoReleaseCancelSpinLock(CancelIrql);
return STATUS_CANCELLED;
}
// Queue the IRP
InsertTailList( &gIRPList, &pIrp->Tail.Overlay.ListEntry);
// Install our cancel-routine
IoMarkIrpPending( pIrp );
IoSetCancelRoutine( pIrp, BrdgCtlCancelIoctl );
NdisDprReleaseSpinLock( &gIRPListLock );
IoReleaseCancelSpinLock( CancelIrql );
return STATUS_PENDING;
}
PIRP
BrdgCtlDequeuePendingIRP()
/*++
Routine Description:
Safely dequeues an IRP on our pending list for use to communicate
a notification
Return Value:
A dequeued IRP if one was available; NULL otherwise.
--*/
{
PLIST_ENTRY Link;
PIRP pIrp = NULL;
while( pIrp == NULL )
{
NdisAcquireSpinLock( &gIRPListLock );
if ( IsListEmpty(&gIRPList) )
{
NdisReleaseSpinLock( &gIRPListLock );
return NULL;
}
// Dequeue a pending IRP
Link = RemoveHeadList( &gIRPList );
pIrp = CONTAINING_RECORD( Link, IRP, Tail.Overlay.ListEntry );
// After this call, it is safe for our cancel routine to call
// RemoveHeadList again on this IRP
InitializeListHead( Link );
// Make the IRP uncancellable so we can complete it.
if( IoSetCancelRoutine( pIrp, NULL ) == NULL )
{
// This IRP must have already been cancelled but our cancel
// routine hasn't gotten control yet. Loop again to get a
// usable IRP.
pIrp = NULL;
}
NdisReleaseSpinLock( &gIRPListLock );
}
return pIrp;
}
VOID
BrdgCtlCancelPendingIRPs()
/*++
Routine Description:
Cancels all pending IRPs
Return Value:
None
--*/
{
PIRP pIrp;
NdisAcquireSpinLock( &gIRPListLock );
while ( !IsListEmpty(&gIRPList) )
{
//
// Take the next IRP off the list
//
pIrp = CONTAINING_RECORD( gIRPList.Flink, IRP, Tail.Overlay.ListEntry );
RemoveEntryList( &pIrp->Tail.Overlay.ListEntry );
// Clean up the ListEntry in case our cancel routine gets called
InitializeListHead( &pIrp->Tail.Overlay.ListEntry );
// Cancel it if necessary
if ( IoSetCancelRoutine( pIrp, NULL ) != NULL )
{
// Our cancel routine will not be called. Complete this IRP ourselves.
NdisReleaseSpinLock( &gIRPListLock );
// Complete the IRP
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
// Resume emptying the list
NdisAcquireSpinLock( &gIRPListLock );
}
// else our cancel routine will be called for this IRP
}
NdisReleaseSpinLock( &gIRPListLock );
}
VOID
BrdgCtlReleaseQueuedNotifications()
/*++
Routine Description:
Frees any queued notifications
Return Value:
None
--*/
{
BSINGLE_LIST_HEAD list;
NdisAcquireSpinLock( &gNotificationsListLock );
// Grab a copy of the whole list head
list = gNotificationsList;
// Set the list head back to empty
BrdgInitializeSingleList( &gNotificationsList );
NdisReleaseSpinLock( &gNotificationsListLock );
// Now free all the items on the list
while( BrdgQuerySingleListLength(&list) > 0L )
{
PDEFERRED_NOTIFY pDeferred = NULL;
PBSINGLE_LIST_ENTRY pList = BrdgRemoveHeadSingleList(&list);
if( pList != NULL )
{
pDeferred = CONTAINING_RECORD( pList, DEFERRED_NOTIFY, List );
NdisFreeMemory( pDeferred, sizeof(DEFERRED_NOTIFY) + pDeferred->DataSize, 0 );
}
else
{
// Should be impossible
SAFEASSERT(FALSE);
}
}
}
VOID
BrdgCtlCleanup()
/*++
Routine Description:
Cleanup routine; called at shutdown
This function is guaranteed to be called exactly once
Return Value:
None
--*/
{
BrdgCtlCancelPendingIRPs();
BrdgCtlReleaseQueuedNotifications();
}