/*++ 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 #include #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(); }