Copyright © 2003 by Printing Communications Assoc., Inc. (PCAUSA). All rights reserved
These are kernel-mode functions and structures associated with opening and closing a handle on a PassThru binding.
There are two important things to notice about this function:
- Case Sensitive Name Comparison
- The NdisEqualMemory function is used to compare the user's binding name with the ADAPT DeviceName field. It would be desirable to use case-insensitive string comparison functions. However, since the comparison is being performed with a spin lock held (IRQL == DISPATCH_LEVEL) string comparison functions are not allowed.
- Reference Count Added To ADAPT Structure
- Notice that a reference count is added to the ADAPT structure before NdisReleaseSpinLock is called. If the ref count was not added, it is entirely possible that NDIS could cause the binding to be unbound (and the ADAPT structure to be freed) before the ADAPT pointer could be returned to the caller.
PADAPT PtLookupAdapterByName( IN PUCHAR pNameBuffer, IN USHORT NameBufferLength, IN BOOLEAN bUseVirtualName ) { PADAPT *ppCursor, pAdapt = NULL;
// // Sanity Checks // if( !pNameBuffer || !NameBufferLength ) { return( NULL ); }
// // Walk The Adapter List // --------------------- // Hold the global lock while walking. Otherwise, the adapter list could be altered at any point in // the list processing sequence. // NdisAcquireSpinLock( &GlobalLock );
for( ppCursor = &pAdaptList; *ppCursor != NULL; ppCursor = &(*ppCursor)->Next ) { __try { if( bUseVirtualName ) { // // Check For Match Against Virtual Adapter Name // if( ( (*ppCursor)->DeviceName.Length == NameBufferLength) && NdisEqualMemory( (*ppCursor)->DeviceName.Buffer, pNameBuffer, NameBufferLength )) { // // Return Pointer To Found Adapter // pAdapt = (*ppCursor); break; } } else { // // Check For Match Against Lower Adapter Name // if( ( (*ppCursor)->LowerDeviceName.Length == NameBufferLength) && NdisEqualMemory( (*ppCursor)->LowerDeviceName.Buffer, pNameBuffer, NameBufferLength)) { // // Return Pointer To Found Adapter // pAdapt = (*ppCursor); break; } } } __except( EXCEPTION_EXECUTE_HANDLER ) { pAdapt = NULL; break; } }
// // Add Reference To Adapter Memory // ------------------------------- // As soon as the spinlock is released (below) and before returning to the caller it is possible // for NDIS to unbind the selected adapter from the PassThru protocol. The reference counting scheme // insures that the memory pointed to by pAdapt will remain valid until the last call to // PtDerefAdapter. // if( pAdapt ) { PtRefAdapter( pAdapt ); }
NdisReleaseSpinLock( &GlobalLock );
return( pAdapt );
}
Used by the driver to manage information about a specific open context (i.e., open handle).
typedef struct _OPEN_CONTEXT { ULONG RefCount; NDIS_SPIN_LOCK Lock; BOOLEAN bAdapterClosed; PADAPT pAdapt; } OPEN_CONTEXT, *POPEN_CONTEXT;
Modify the ADAPT structure to add a member variable that points to the OPEN_CONTEXT structure associated with the adapter..
typedef struct _ADAPT { ... NDIS_STRING DeviceName; // For initializing the miniport edge // BEGIN_PTUSERIO ULONG RefCount; // Used For ADAPT Reference Counting POPEN_CONTEXT pOpenContext; // END_PTUSERIO NDIS_EVENT MiniportInitEvent; // For blocking UnbindAdapter while // an IM Init is in progress. ... } ADAPT, *PADAPT;
These are the two OPEN_CONTEXT structure reference counting functions. It manages when the OPEN_CONTEXT structure is freed..
VOID
DevRefOpenContext( POPEN_CONTEXT pOpenContext )
{
PtRefAdapter( pOpenContext->pAdapt );
NdisInterlockedIncrement( &pOpenContext->RefCount ); }
VOID DevDerefOpenContext( POPEN_CONTEXT pOpenContext ) { PADAPT pAdapt = NULL;
if( !pOpenContext ) { return; }
pAdapt = pOpenContext->pAdapt;
if( NdisInterlockedDecrement( &pOpenContext->RefCount) == 0 ) { NdisFreeSpinLock( &pOpenContext->Lock );
NdisFreeMemory(pOpenContext, 0, 0); }
PtDerefAdapter( pAdapt ); }
This function allocates and initializes an OPEN_CONTEXT structure.
POPEN_CONTEXT DevAllocateOpenContext( PADAPT pAdapt ) { POPEN_CONTEXT pOpenContext = NULL;
NdisAllocateMemoryWithTag( &pOpenContext, sizeof( OPEN_CONTEXT ), TAG );
if( !pOpenContext ) { return( NULL ); }
// // Initialize The Open Context Structure // NdisZeroMemory( pOpenContext, sizeof( OPEN_CONTEXT ) );
NdisAllocateSpinLock( &pOpenContext->Lock );
NdisInitializeEvent( &pOpenContext->LocalRequest.RequestEvent );
// // Add Initial Reference To Open Context // ------------------------------------- // Note that we already have added an implicit reference to the adapter // because of the PtLookupAdapterByName call. // pOpenContext->RefCount = 1;
pOpenContext->pAdapt = pAdapt;
return( pOpenContext ); }
The DevIoControl dispatcher calls the DevOpenAdapter function to do the work related to opening an adapter handle.
NTSTATUS DevOpenAdapter( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp, IN BOOLEAN bUseVirtualName ) { PIO_STACK_LOCATION pIrpSp; NTSTATUS NtStatus = STATUS_SUCCESS; ULONG BytesReturned = 0; PUCHAR pNameBuffer = NULL; ULONG NameBufferLength; PADAPT pAdapt; POPEN_CONTEXT pOpenContext;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pNameBuffer = pIrp->AssociatedIrp.SystemBuffer; NameBufferLength = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
// // Lookup Adapter By Name // ---------------------- // If successful the lookup function has added a ref count to the found ADAPT // structure. // pAdapt = PtLookupAdapterByName( pNameBuffer, (USHORT )NameBufferLength, bUseVirtualName );
if( !pAdapt ) { NtStatus = STATUS_OBJECT_NAME_NOT_FOUND; goto CompleteTheIRP; }
// // Fail Open If Unbind Is In Progress // NdisAcquireSpinLock(&pAdapt->Lock); if( pAdapt->UnbindingInProcess ) { NdisReleaseSpinLock(&pAdapt->Lock); PtDerefAdapter( pAdapt );
NtStatus = STATUS_INVALID_DEVICE_STATE; goto CompleteTheIRP; } NdisReleaseSpinLock(&pAdapt->Lock);
if( pAdapt->pOpenContext ) { PtDerefAdapter( pAdapt );
NtStatus = STATUS_DEVICE_BUSY; goto CompleteTheIRP; }
pOpenContext = DevAllocateOpenContext( pAdapt );
if( !pOpenContext ) { PtDerefAdapter( pAdapt );
NtStatus = STATUS_INSUFFICIENT_RESOURCES; goto CompleteTheIRP; }
// // Sanity Check For Concurrent Open Race Condition // ----------------------------------------------- // At this point we enforce exclusive access on a per-binding basis. // // This logic deals with the situation where two concurrent adapter // opens could be in progress. We want an atomic mechanism that insures // that only one of the opens will be successful. // // This InterlockedXXX function performs an atomic operation: First it // compares pAdapt->pOpenContext with NULL, if they are equal, the function // puts pOpenContext into pAdapt->pOpenContext, and return NULL. Otherwise, // it return existing pAdapt->pOpenContext without changing anything. // // NOTE: This implementation is borrowed from the NDISPROT sample from // the Windows DDK. // if ( InterlockedCompareExchangePointer (& (pAdapt->pOpenContext), pOpenContext, NULL) != NULL) { PtDerefAdapter( pAdapt );
NtStatus = STATUS_DEVICE_BUSY; goto CompleteTheIRP; }
// // Associate This Handle With The Open Context // pIrpSp->FileObject->FsContext = pOpenContext;
// // Complete The IRP // CompleteTheIRP:
pIrp->IoStatus.Information = BytesReturned; pIrp->IoStatus.Status = NtStatus; IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return NtStatus; }
In DevIoControl we add a call to dispatch IOCTL_PTUSERIO_OPEN_ADAPTER (defined in IOCommon.h) to the DevOpenAdapter handler
NTSTATUS DevIoControl( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) { PIO_STACK_LOCATION pIrpSp; NTSTATUS NtStatus = STATUS_SUCCESS; ULONG BytesReturned = 0; ULONG FunctionCode; PUCHAR ioBuffer = NULL; ULONG inputBufferLength; ULONG outputBufferLength;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
ioBuffer = pIrp->AssociatedIrp.SystemBuffer; inputBufferLength = pIrpSp->Parameters.DeviceIoControl.InputBufferLength; outputBufferLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
FunctionCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode;
switch (FunctionCode) { case IOCTL_PTUSERIO_ENUMERATE: return( DevEnumerateBindings( pDeviceObject, pIrp ) );
case IOCTL_PTUSERIO_OPEN_ADAPTER: return( DevOpenAdapter( pDeviceObject, pIrp, FALSE // Is Lower Adapter ) );
case IOCTL_PTUSERIO_QUERY_INFORMATION: case IOCTL_PTUSERIO_SET_INFORMATION: default: NtStatus = STATUS_NOT_SUPPORTED; break; }
if (NtStatus != STATUS_PENDING) { pIrp->IoStatus.Information = BytesReturned; pIrp->IoStatus.Status = NtStatus; IoCompleteRequest(pIrp, IO_NO_INCREMENT); }
return NtStatus; }
Here we null some pointers and call DefDerefOpenContext.
NTSTATUS DevClose( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) { PIO_STACK_LOCATION pIrpSp; NTSTATUS NtStatus = STATUS_SUCCESS; POPEN_CONTEXT pOpenContext; pIrpSp = IoGetCurrentIrpStackLocation(pIrp); pOpenContext = pIrpSp->FileObject->FsContext; // // Undo IRP_MJ_CREATE Operations // pIrpSp->FileObject->FsContext = NULL; pIrpSp->FileObject->FsContext2 = NULL;
if( pOpenContext ) { if( pOpenContext->pAdapt ) { (pOpenContext->pAdapt)->pOpenContext = NULL; }
DevDerefOpenContext( pOpenC ontext ); } pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = NtStatus; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return NtStatus; }
This function is called from MPHalt or PtUnbindAdapter to notify the open handle logic of a "surprise unbind" event.
VOID DevOnUnbindAdapter( POPEN_CONTEXT pOpenContext ) { PADAPT pAdapt = NULL;
if( !pOpenContext ) { return; }
DBGPRINT(("==>Pt DevOnUnbindAdapter: Context %p\n", pOpenContext )); // // Set Flag That Will Cause Subsequent I/O To Be failed // pOpenContext->bAdapterClosed = TRUE;
// // Wait For Outstanding NDIS Operations On The Handle To Complete // // // Cancel Pending I/O On The Handle // DBGPRINT(("<== Pt DevOnUnbindAdapter\n")); }
This function is modified to call DevOnUnbindAdapter to notify the open handle logic of a "surprise unbinding" event.
VOID MPHalt( IN NDIS_HANDLE MiniportAdapterContext ) { ... NdisReleaseSpinLock(&GlobalLock);
// BEGIN_PTUSERIO // // Notify Open Handle Logic About Surprise Inbind // DevOnUnbindAdapter( pAdapt->pOpenContext ); // END_PTUSERIO
// // Delete the ioctl interface that was created when the miniport // was created. // (VOID)PtDeregisterDevice(); ... }
This function is modified to call DevOnUnbindAdapter to notify the open handle logic of a "surprise unbinding" event.
VOID PtUnbindAdapter( OUT PNDIS_STATUS Status, IN NDIS_HANDLE ProtocolBindingContext, IN NDIS_HANDLE UnbindContext ) {
... else { NdisReleaseSpinLock(&pAdapt->Lock); }
// BEGIN_PTUSERIO // // Notify Open Handle Logic About Surprise Inbind // DevOnUnbindAdapter( pAdapt->pOpenContext ); // END_PTUSERIO
#ifndef WIN9X // // Check if we had called NdisIMInitializeDeviceInstanceEx and // we are awaiting a call to MiniportInitialize. // ... }