Kernel-Mode
Adapter Handle Code Fragments

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.

New Code: PTExtend.c Module, PtLookupAdapterByName Function

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 );
}
 

New Code: PTExtend.h Module, OPEN_CONTEXT Structure

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;

 

Modified Code: PassThru.h Module, ADAPT Structure

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;

 

New Code: PTExtend.c Module, DevRefOpenContext and DevDerefOpenContext Functions

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 );
}

New Code: PTExtend.c Module, DevAllocateOpenContext Function

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 );
}

New Code: PTExtend.c Module, DevOpenAdapter Function

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;
}

Modified Code: PTExtend.c Module, DevIoControl Function

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;
} 

Modified Code: PTExtend.c Module, DevClose Function

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;
} 

 

Modified Code: PTExtend.c Module, DevOnUnbindAdapter Function

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"));
}

 

Modified Code: Miniport.c Module, MPHalt Function

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();
    ...
}

 

Modified Code: Protocol.c Module, PtUnbindAdapter Function

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.
    //
    ...
}