Kernel-Mode
Binding Enumeration Code Fragments

Copyright © 2003 by Printing Communications Assoc., Inc. (PCAUSA). All rights reserved

The essential work of saving binding names is already done in the baseline PassThru driver. All we need to do is to add a handler for IOCTL_PTUSERIO_ENUMERATE that fills the user's buffer with the binding names.

The binding names are saved in the ADAPT structure that is created by the PtBindAdapter function for each successfully opened binding. The virtual adapter name (name passed to NdisIMInitializeDeviceInstanceEx) is saved in the DeviceName field of the ADAPT structure.

The ADAPT structures themselves are kept in a singly-linked list whose list head is the global variable pAdaptList.

All we have to do to implement the PassThruEx binding enumeration function is fill a user-provided buffer with DeviceName strings fetched from the list of ADAPT structures. In the PTExtend module we add a function called DevEnumerateBindings to fill the user's buffer with binding names. This function is called from the DevIoControl IOCTL dispatcher for the case IOCTL_PTUSERIO_ENUMERATE.

The buffer will be filled with an array of null-terminated Unicode strings with the end of the list being identified by an empty Unicode string.

There are a few additional details to attend to. It is necessary to account for the possibility that the adapter list could change while we were walking the list and copying binding names into the user's buffer. The baseline PassThru driver already has a spinlock for this purpose. We simply hold the GlobalLock while we are examining the adapter list. In addition, add appropriate sanity checks and __try/__except handlers to head off potential problems.

Below is the basic code for the driver DevEnumerateBindings function that fills a user's buffer with binding names. Further down on this page is the DevIoControl handler which has been expanded to call DevEnumerateBindings in response to receiving the IOCTL_PTUSERIO_ENUMERATE IOCTL function code.

 

NTSTATUS
DevEnumerateBindings(
   IN PDEVICE_OBJECT    pDeviceObject,
   IN PIRP              pIrp
   )
{
   PIO_STACK_LOCATION  pIrpSp;
   NTSTATUS            NtStatus = STATUS_SUCCESS;
   ULONG               BytesReturned = 0;
   PUCHAR              ioBuffer = NULL;
   ULONG               inputBufferLength;
   ULONG               outputBufferLength, Remaining;
   PADAPT              *ppCursor;
   pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
   ioBuffer = pIrp->AssociatedIrp.SystemBuffer;
   inputBufferLength  = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
   outputBufferLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
   Remaining = outputBufferLength;
   //
   // Sanity Check On Length
   //
   if( sizeof( UNICODE_NULL ) > Remaining )
   {
      BytesReturned = 0;
      NtStatus = NDIS_STATUS_BUFFER_OVERFLOW;
      goto CompleteTheIRP;
   }
   //
   // Walk The Adapter List
   //
   NdisAcquireSpinLock( &GlobalLock );
   __try
   {
      //
      // Insert List-Terminating NULL
      //
      *((PWCHAR )ioBuffer) = UNICODE_NULL;
      BytesReturned = sizeof( UNICODE_NULL );
      Remaining -= sizeof( UNICODE_NULL );
      for( ppCursor = &pAdaptList; *ppCursor != NULL;
         ppCursor = &(*ppCursor)->Next
         )
      {
         //
         // Sanity Check On Length
         //
         if( (*ppCursor)->DeviceName.Length + sizeof( UNICODE_NULL) > Remaining )
         {
            BytesReturned = 0;
            NtStatus = NDIS_STATUS_BUFFER_OVERFLOW;
            break;
         }
         //
         // Add The Virtual DeviceName To The Buffer
         // ----------------------------------------
         // This name passed to NdisIMInitializeDeviceInstanceEx.
         //
         NdisMoveMemory(
            ioBuffer,
            (*ppCursor)->DeviceName.Buffer,
            (*ppCursor)->DeviceName.Length
            );
         //
         // Move Past Virtual DeviceName In Buffer
         //
         Remaining -= (*ppCursor)->DeviceName.Length;
         BytesReturned += (*ppCursor)->DeviceName.Length;
         ioBuffer += (*ppCursor)->DeviceName.Length;
         //
         // Add Name-Terminating NULL
         //
         *((PWCHAR )ioBuffer) = UNICODE_NULL;
         Remaining -= sizeof( UNICODE_NULL );
         BytesReturned += sizeof( UNICODE_NULL );
         ioBuffer += sizeof( UNICODE_NULL );
         //
         // Add List-Terminating NULL
         // -------------------------
         // Space is already accomodated for this.
         //
         *((PWCHAR )ioBuffer) = UNICODE_NULL;
      }
   }
   __except( EXCEPTION_EXECUTE_HANDLER )
   {
      BytesReturned = 0;
      NtStatus = STATUS_INVALID_PARAMETER;
   }
   NdisReleaseSpinLock( &GlobalLock );
CompleteTheIRP:
   if (NtStatus != STATUS_PENDING)
   {
      pIrp->IoStatus.Information = BytesReturned;
      pIrp->IoStatus.Status = NtStatus;
      IoCompleteRequest(pIrp, IO_NO_INCREMENT);
   }
   return NtStatus;
}
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;
    
    UNREFERENCED_PARAMETER(pDeviceObject);
    
    pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
    
    ioBuffer = pIrp->AssociatedIrp.SystemBuffer;
    inputBufferLength  = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
    outputBufferLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
    
    FunctionCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode;
    
    DBGPRINT(("==>Pt DevIoControl: FileObject %p\n", pIrpSp->FileObject ));
    
    switch (FunctionCode)
    {
        case IOCTL_PTUSERIO_ENUMERATE:
         return( DevEnumerateBindings(
                  pDeviceObject,
                  pIrp
                  )
               );
        case IOCTL_PTUSERIO_OPEN_LOWER_ADAPTER:
        case IOCTL_PTUSERIO_OPEN_VIRTUAL_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);
    }
    
    DBGPRINT(("<== Pt DevIoControl\n"));
    
    return NtStatus;
}