text: /*++
  
  Copyright (c) 1997 SCM Microsystems, Inc.
  
  Module Name:
  
      usbcom.c
  
  Abstract:
  
     Hardware access functions for USB smartcard reader
  
  
  Environment:
  
        WDM
  
  Revision History:
  
     PP       01/19/1999  1.01
     PP       12/18/1998  1.00  Initial Version
  
  
  --*/
  
  
  #include "common.h"
  #include "stcCmd.h"
  #include "usbcom.h"
  #include "stcusbnt.h"
  
  #pragma optimize( "", off )
  
  
  
  NTSTATUS STCtoNT(
     UCHAR ucData[])
  /*++
  
  Routine Description:
     Error code translation routine
  
  Arguments:
     ucData   Error code returned by the STC
  
  Return Value:
     Corresponding NT error code
  
  --*/
  {
     USHORT usCode = ucData[0]*0x100 +ucData[1];
     NTSTATUS NtStatus;
  
  
     switch (usCode)
     {
        case 0x9000:
           NtStatus = STATUS_SUCCESS;
           break;
        case 0x5800:
           NtStatus = STATUS_UNSUCCESSFUL;
           break;
        case 0x2000:
           NtStatus = STATUS_UNSUCCESSFUL;
           break;
        case 0x4000:
           NtStatus = STATUS_UNSUCCESSFUL;
           break;
        case 0x64A1:
           NtStatus = STATUS_NO_MEDIA;
           break;
        case 0x64A0:
           NtStatus = STATUS_MEDIA_CHANGED;
           break;
        case 0x6203:
           NtStatus = STATUS_UNSUCCESSFUL;
           break;
        case 0x6300:
           NtStatus = STATUS_UNSUCCESSFUL;
           break;
        case 0x6500:
           NtStatus = STATUS_UNSUCCESSFUL;
           break;
        case 0x6A00:
           NtStatus = STATUS_UNSUCCESSFUL;
           break;
        case 0x6A80:
           NtStatus = STATUS_UNSUCCESSFUL;
           break;
        default:
           NtStatus = STATUS_UNSUCCESSFUL;
           break;
     }
     return(NtStatus);
  }
  
  
  //******************************************************************************
  //
  // UsbSyncCompletionRoutine()
  //
  // Completion routine used by UsbCallUSBD.
  //
  // Signals an Irp completion event and then returns MORE_PROCESSING_REQUIRED
  // to stop further completion of the Irp.
  //
  // If the Irp is one we allocated ourself, DeviceObject is NULL.
  //
  //******************************************************************************
  
  NTSTATUS
  UsbSyncCompletionRoutine (
      IN PDEVICE_OBJECT   DeviceObject,
      IN PIRP             Irp,
      IN PVOID            Context
      )
  {
      PKEVENT kevent;
  
      kevent = (PKEVENT)Context;
  
      KeSetEvent(kevent,
                 IO_NO_INCREMENT,
                 FALSE);
  
      return STATUS_MORE_PROCESSING_REQUIRED;
  }
  
  //******************************************************************************
  //
  // UsbCallUSBD()
  //
  // Synchronously sends a URB down the device stack.  Blocks until the request
  // completes normally or until the request is timed out and cancelled.
  //
  // Must be called at IRQL PASSIVE_LEVEL
  //
  //******************************************************************************
  
  NTSTATUS
  UsbCallUSBD (
      IN PDEVICE_OBJECT   DeviceObject,
      IN PURB             Urb
      )
  {
      PDEVICE_EXTENSION   deviceExtension;
      KEVENT              localevent;
      PIRP                irp;
      PIO_STACK_LOCATION  nextStack;
      NTSTATUS            ntStatus;
  
      deviceExtension = DeviceObject->DeviceExtension;
  
      // Initialize the event we'll wait on
      //
      KeInitializeEvent(&localevent,
                        SynchronizationEvent,
                        FALSE);
  
      // Allocate the Irp
      //
      irp = IoAllocateIrp(deviceExtension->AttachedPDO->StackSize,
                          FALSE);
  
      if (irp == NULL)
      {
          return STATUS_INSUFFICIENT_RESOURCES;
      }
  
      // Set the Irp parameters
      //
      nextStack = IoGetNextIrpStackLocation(irp);
  
      nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
  
      nextStack->Parameters.DeviceIoControl.IoControlCode =
          IOCTL_INTERNAL_USB_SUBMIT_URB;
  
      nextStack->Parameters.Others.Argument1 = Urb;
  
      // Set the completion routine, which will signal the event
      //
      IoSetCompletionRoutine(irp,
                             UsbSyncCompletionRoutine,
                             &localevent,
                             TRUE,    // InvokeOnSuccess
                             TRUE,    // InvokeOnError
                             TRUE);   // InvokeOnCancel
  
      // Pass the Irp & Urb down the stack
      //
      ntStatus = IoCallDriver(deviceExtension->AttachedPDO,
                              irp);
  
      // If the request is pending, block until it completes
      //
      if (ntStatus == STATUS_PENDING)
      {
          LARGE_INTEGER timeout;
  
          // We used to wait for 1 second, but that made this timeout longer
          // than the polling period of 500ms.  So, if this read failed (e.g.,
          // because of device or USB failure) and timeout, two more worker items
          // would get queued and eventually hundreds of working items would be
          // backed up.  By reducing this timeout we have a good chance that this
          // will finish before the next item is queued.  450ms seems a good value.
          //
          timeout.QuadPart = -4500000; // 450ms
  
          ntStatus = KeWaitForSingleObject(&localevent,
                                           Executive,
                                           KernelMode,
                                           FALSE,
                                           &timeout);
  
          if (ntStatus == STATUS_TIMEOUT)
          {
              ntStatus = STATUS_IO_TIMEOUT;
  
              // Cancel the Irp we just sent.
              //
              IoCancelIrp(irp);
  
              // And wait until the cancel completes
              //
              KeWaitForSingleObject(&localevent,
                                    Executive,
                                    KernelMode,
                                    FALSE,
                                    NULL);
          }
          else
          {
              ntStatus = irp->IoStatus.Status;
          }
      }
  
      // Done with the Irp, now free it.
      //
      IoFreeIrp(irp);
  
      return ntStatus;
  }
  
  NTSTATUS
  UsbSelectInterfaces(
     IN PDEVICE_OBJECT DeviceObject,
     IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
  /*++
  
  Routine Description:
      Initializes an USB reader with (possibly) multiple interfaces;
     This driver only supports one interface (with multiple endpoints).
  
  
  Arguments:
      DeviceObject - pointer to the device object for this instance of the device.
  
      ConfigurationDescriptor - pointer to the USB configuration
                      descriptor containing the interface and endpoint
                      descriptors.
  
  
  Return Value:
  
      NT status code
  
  --*/
  {
     PDEVICE_EXTENSION DeviceExtension= DeviceObject->DeviceExtension;
     NTSTATUS NtStatus;
     PURB pUrb = NULL;
     USHORT usSize;
     ULONG  ulNumberOfInterfaces, i;
     UCHAR ucNumberOfPipes, ucAlternateSetting, ucMyInterfaceNumber;
     PUSB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
     PUSBD_INTERFACE_INFORMATION InterfaceObject;
  
      // This driver only supports one interface, we must parse
      // the configuration descriptor for the interface
      // and remember the pipes.
      //
  
      pUrb = USBD_CreateConfigurationRequest(ConfigurationDescriptor, &usSize);
  
     if (pUrb)
     {
        //
        // USBD_ParseConfigurationDescriptorEx searches a given configuration
        // descriptor and returns a pointer to an interface that matches the
        //  given search criteria. We only support one interface on this device
        //
          InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
           ConfigurationDescriptor,
           ConfigurationDescriptor, //search from start of config  descriptro
           -1,   // interface number not a criteria; we only support one interface
           -1,   // not interested in alternate setting here either
           -1,   // interface class not a criteria
           -1,   // interface subclass not a criteria
           -1);  // interface protocol not a criteria
  
        ASSERT( InterfaceDescriptor != NULL );
  
        InterfaceObject = &pUrb->UrbSelectConfiguration.Interface;
  
        for (i = 0; i < InterfaceObject->NumberOfPipes; i++)
        {
           InterfaceObject->Pipes[i].PipeFlags = 0;
          }
  
          UsbBuildSelectConfigurationRequest(
           pUrb,
           usSize,
           ConfigurationDescriptor);
  
        NtStatus = UsbCallUSBD(DeviceObject, pUrb);
      }
     else
     {
          NtStatus = STATUS_INSUFFICIENT_RESOURCES;
      }
  
      if(NtStatus == STATUS_SUCCESS)
     {
  
        // Save the configuration handle for this device
          DeviceExtension->ConfigurationHandle =
              pUrb->UrbSelectConfiguration.ConfigurationHandle;
  
        ASSERT(DeviceExtension->Interface == NULL);
  
          DeviceExtension->Interface = ExAllocatePool(
           NonPagedPool,
              InterfaceObject->Length
           );
  
          if (DeviceExtension->Interface)
        {
              // save a copy of the interface information returned
              RtlCopyMemory(
              DeviceExtension->Interface,
              InterfaceObject,
              InterfaceObject->Length);
        }
        else
        {
           NtStatus = STATUS_NO_MEMORY;
        }
      }
  
      if (pUrb)
     {
          ExFreePool(pUrb);
      }
  
      return NtStatus;
  }
  
  NTSTATUS
  UsbConfigureDevice(
     IN PDEVICE_OBJECT DeviceObject)
  /*++
  
  Routine Description:
      Initializes a given instance of the device on the USB and
     selects and saves the configuration.
  
  Arguments:
  
     DeviceObject - pointer to the physical device object for this instance of the device.
  
  
  Return Value:
  
      NT status code
  
  
  --*/
  {
     PDEVICE_EXTENSION DeviceExtension= DeviceObject->DeviceExtension;
     NTSTATUS NtStatus;
     PURB pUrb = NULL;
     ULONG ulSize;
     PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor = NULL;
  
     __try {
  
        pUrb = ExAllocatePool(
           NonPagedPool,
           sizeof( struct _URB_CONTROL_DESCRIPTOR_REQUEST )
           );
  
        if( pUrb == NULL)
        {
           NtStatus = STATUS_NO_MEMORY;
           __leave;
        }
  
        // When USB_CONFIGURATION_DESCRIPTOR_TYPE is specified for DescriptorType
        // in a call to UsbBuildGetDescriptorRequest(),
        // all interface, endpoint, class-specific, and vendor-specific descriptors
        // for the configuration also are retrieved.
        // The caller must allocate a buffer large enough to hold all of this
        // information or the data is truncated without error.
        // Therefore the 'siz' set below is just a 'good guess', and we may have to retry
          ulSize = sizeof( USB_CONFIGURATION_DESCRIPTOR ) + 16;
  
         // We will break out of this 'retry loop' when UsbBuildGetDescriptorRequest()
        // has a big enough deviceExtension->UsbConfigurationDescriptor buffer not to truncate
        while( 1 )
        {
           ConfigurationDescriptor = ExAllocatePool( NonPagedPool, ulSize );
  
           if(ConfigurationDescriptor == NULL)
           {
              NtStatus = STATUS_NO_MEMORY;
              __leave;
           }
  
           UsbBuildGetDescriptorRequest(
              pUrb,
              sizeof( struct _URB_CONTROL_DESCRIPTOR_REQUEST ),
              USB_CONFIGURATION_DESCRIPTOR_TYPE,
              0,
              0,
              ConfigurationDescriptor,
              NULL,
              ulSize,
              NULL );
  
           NtStatus = UsbCallUSBD( DeviceObject, pUrb );
  
           // if we got some data see if it was enough.
           // NOTE: we may get an error in URB because of buffer overrun
           if (pUrb->UrbControlDescriptorRequest.TransferBufferLength == 0 ||
              ConfigurationDescriptor->wTotalLength <= ulSize)
           {
              break;
           }
  
           ulSize = ConfigurationDescriptor->wTotalLength;
           ExFreePool(ConfigurationDescriptor);
           ConfigurationDescriptor = NULL;
        }
  
        //
        // We have the configuration descriptor for the configuration we want.
        // Now we issue the select configuration command to get
        // the  pipes associated with this configuration.
        //
        if(NT_SUCCESS(NtStatus))
        {
            NtStatus = UsbSelectInterfaces(
               DeviceObject,
               ConfigurationDescriptor);
        }
     }
     __finally {
  
        if( pUrb )
        {
           ExFreePool( pUrb );
        }
        if( ConfigurationDescriptor )
        {
           ExFreePool( ConfigurationDescriptor );
        }
     }
  
     return NtStatus;
  }
  
  NTSTATUS
  UsbWriteSTCData(
     PREADER_EXTENSION ReaderExtension,
     PUCHAR            pucData,
     ULONG          ulSize)
  
  /*++
  
  Routine Description:
     Write data in the STC
  
  Arguments:
     ReaderExtension   Context of the call
     APDU        Buffer to write
     ulAPDULen      Length of the buffer to write
  Return Value:
  
  --*/
  {
     NTSTATUS NTStatus = STATUS_SUCCESS;
     PUCHAR pucCmd;
     UCHAR ucResponse[3];
     BOOLEAN resend = TRUE;
     LONG Len;
     ULONG Index;
     LONG refLen = (LONG) ulSize;
     ULONG Retries;
  
     pucCmd = ReaderExtension->pExtBuffer;
  
     ReaderExtension->ulReadBufferLen = 0;
  
     // Build the write data command
     Len = refLen;
     Index = 0;
  
     while (resend == TRUE)
     {
        if(Len > 62)
        {
           Len = 62;
           resend = TRUE;
        }
        else
        {
           resend = FALSE;
        }
  
        *pucCmd = 0xA0;
        *(pucCmd+1) = (UCHAR) Len;
        memcpy( pucCmd + 2, pucData+Index, Len );
  
            Retries = USB_WRITE_RETRIES;
            do
            {
           // Send the Write data command
           NTStatus = UsbWrite( ReaderExtension, pucCmd, 2 + Len);
           if (NTStatus != STATUS_SUCCESS)
                   {
              SmartcardDebug(
                 DEBUG_DRIVER,
                 ("%s!UsbWriteSTCData: write error %X \n",
                 DRIVER_NAME,
                 NTStatus)
                 );
              break;
                   }
           // Read the response
           NTStatus = UsbRead( ReaderExtension, ucResponse, 3);
           if (NTStatus != STATUS_SUCCESS)
                   {
              SmartcardDebug(
                 DEBUG_DRIVER,
                 ("%s!UsbWriteSTCData: read error %X \n",
                 DRIVER_NAME,
                 NTStatus)
                 );
              break;
                   }
           else
                   {
               // Test if what we read is really a response to a write
              if(ucResponse[0] != 0xA0)
                          {
                 NTStatus = STCtoNT(ucResponse);
                          }
                   }
            } while(( NTStatus != STATUS_SUCCESS ) && --Retries );
  
            if( NTStatus != STATUS_SUCCESS )
                    break;
  
        Index += 62;
        Len = refLen - 62;
        refLen = refLen - 62;
     }
  
     return STATUS_SUCCESS;
  }
  
  NTSTATUS
  UsbReadSTCData(
     PREADER_EXTENSION    ReaderExtension,
     PUCHAR               pucData,
     ULONG             ulDataLen)
  
  /*++
  
  Routine Description:
     Read data from the STC
  
  Arguments:
     ReaderExtension   Context of the call
     ulAPDULen      Length of the buffer to write
     pucData        Output Buffer
  
  
  Return Value:
  
  --*/
  {
     NTSTATUS NTStatus = STATUS_SUCCESS;
     UCHAR       ucCmd[1];
     PUCHAR      pucResponse;
     int i;
     ULONG ulLenExpected = ulDataLen;
     ULONG Index=0;
     BOOLEAN  SendReadCommand = TRUE;
     LARGE_INTEGER  Begin;
     LARGE_INTEGER  End;
  
     pucResponse  = ReaderExtension->pExtBuffer;
  
     KeQuerySystemTime( &Begin );
     End = Begin;
     End.QuadPart = End.QuadPart + (LONGLONG)10 * 1000 * ReaderExtension->ReadTimeout;
  
     // First let see if we have not already read the data that
     // we need
     if(ReaderExtension->ulReadBufferLen != 0)
     {
        if(ReaderExtension->ulReadBufferLen >= ulLenExpected)
        {
           // all the data that we need are available
           memcpy(pucData,ReaderExtension->ucReadBuffer,ulLenExpected);
           ReaderExtension->ulReadBufferLen = ReaderExtension->ulReadBufferLen - ulLenExpected;
           if(ReaderExtension->ulReadBufferLen != 0)
           {
              memcpy(
                 ReaderExtension->ucReadBuffer,
                 ReaderExtension->ucReadBuffer+ulLenExpected,
                 ReaderExtension->ulReadBufferLen);
           }
           SendReadCommand = FALSE;
        }
        else
        {
           // all the data that we need are not available
           memcpy(pucData,ReaderExtension->ucReadBuffer,ReaderExtension->ulReadBufferLen);
           ulLenExpected = ulLenExpected - ReaderExtension->ulReadBufferLen;
           Index = ReaderExtension->ulReadBufferLen;
           ReaderExtension->ulReadBufferLen = 0;
           SendReadCommand = TRUE;
        }
     }
     while( SendReadCommand == TRUE)
     {
        // Build the Read Register command
        ucCmd[0] = 0xE0;
  
        NTStatus = UsbWrite( ReaderExtension, ucCmd, 1);
        if (NTStatus != STATUS_SUCCESS)
        {
           SmartcardDebug(
              DEBUG_DRIVER,
              ("%s!UsbReadSTCData: write error %X \n",
              DRIVER_NAME,
              NTStatus)
              );
           break;
        }
  
        NTStatus = UsbRead( ReaderExtension, pucResponse, 64);
        if (NTStatus != STATUS_SUCCESS)
        {
           SmartcardDebug(
              DEBUG_DRIVER,
              ("%s!UsbReadSTCData: read error %X \n",
              DRIVER_NAME,
              NTStatus)
              );
           break;
        }
        // Test if what we read is really a READ DATA frame
        if(*pucResponse != 0xE0)
        {
           if(*pucResponse == 0x64 && *(pucResponse + 1) == 0xA0)
           {
              NTStatus = STATUS_NO_MEDIA;
           }
           else
           {
              NTStatus = STCtoNT(pucResponse);
           }
           break;
        }
        // If there is no data available
        if (*(pucResponse + 1) == 0)
        {
           KeQuerySystemTime( &Begin );
           if(RtlLargeIntegerGreaterThan(End, Begin))
           {
              SendReadCommand = TRUE;
           }
           else
           {
              ReaderExtension->ulReadBufferLen = 0;
              SmartcardDebug(
                 DEBUG_DRIVER,
                 ("%s!UsbReadSTCData: Timeout %X \n",
                 DRIVER_NAME,
                 STATUS_IO_TIMEOUT));
              NTStatus =STATUS_IO_TIMEOUT;
              break;
           }
        }
        if ((ULONG) *(pucResponse+1) < ulLenExpected)
        {
           memcpy(pucData+Index,pucResponse+2,(ULONG) *(pucResponse+1));
           Index = Index + (ULONG) *(pucResponse+1);
           ulLenExpected = ulLenExpected - (ULONG) *(pucResponse+1);
           SendReadCommand = TRUE;
        }
        else
        {
           SendReadCommand = FALSE;
           memcpy(pucData+Index,pucResponse+2,ulLenExpected);
  
           if((ULONG) *(pucResponse+1) > ulLenExpected)
           {
              memcpy(
                 ReaderExtension->ucReadBuffer,
                 pucResponse+ulLenExpected+2,
                 (ULONG) *(pucResponse+1) - ulLenExpected);
  
              ReaderExtension->ulReadBufferLen =
                 (ULONG) *(pucResponse+1) - ulLenExpected;
           }
           else
           {
              ReaderExtension->ulReadBufferLen = 0;
           }
        }
     }
  
     return NTStatus;
  }
  
  NTSTATUS
  UsbWriteSTCRegister(
     PREADER_EXTENSION ReaderExtension,
     UCHAR          ucAddress,
     ULONG          ulSize,
     PUCHAR            pucValue)
  /*++
  
  Routine Description:
  
  Arguments:
  
  Return Value:
  
  --*/
  {
     NTSTATUS NTStatus = STATUS_SUCCESS;
     PUCHAR pucCmd;
     UCHAR ucResponse[2];
  
     if(ulSize > 16)
     {
        return STATUS_UNSUCCESSFUL;
     }
  
     pucCmd = ReaderExtension->pExtBuffer;
  
     ReaderExtension->ulReadBufferLen = 0;
  
     // Build the write register command
     *pucCmd = 0x80 | ucAddress;
     *(pucCmd+1) = (UCHAR) ulSize;
     memcpy( pucCmd + 2, pucValue, ulSize );
  
     // Send the Write Register command
     NTStatus = UsbWrite( ReaderExtension, pucCmd, 2 + ulSize);
     if (NTStatus == STATUS_SUCCESS)
     {
        // Read the acknowledge
        NTStatus = UsbRead( ReaderExtension, ucResponse, 2);
        if (NTStatus == STATUS_SUCCESS)
        {
           NTStatus = STCtoNT(ucResponse);
        }
     }
  
     return NTStatus;
  }
  
  NTSTATUS
  UsbReadSTCRegister(
     PREADER_EXTENSION ReaderExtension,
     UCHAR          ucAddress,
     ULONG          ulSize,
     PUCHAR            pucValue)
  /*++
  
  Routine Description:
  
  Arguments:
  
  Return Value:
  
  --*/
  {
     NTSTATUS NTStatus = STATUS_SUCCESS;
     UCHAR       ucCmd[2];
     PUCHAR      pucResponse;
  
     if(ulSize > 16)
     {
        return STATUS_UNSUCCESSFUL;
     }
  
     pucResponse = ReaderExtension->pExtBuffer;
  
     // Build the Read Register command
     ucCmd[0] = 0xC0 | ucAddress;
     ucCmd[1] = (UCHAR) ulSize;
  
     // Send the Read Register command
     NTStatus = UsbWrite( ReaderExtension, ucCmd, 2);
     if (NTStatus == STATUS_SUCCESS)
     {
        // Read the response from the reader
        NTStatus = UsbRead(
           ReaderExtension,
           pucResponse,
           6
           );
  
        if (NTStatus == STATUS_SUCCESS)
        {
           // Test if what we read is really a READ frame
           if(*pucResponse == 0x21)
           {
              if(*(pucResponse + 1) > 4)
              {
                 NTStatus = STATUS_BUFFER_TOO_SMALL;
              }
              else
              {
                 memcpy(
                    pucValue,
                    pucResponse + 2,
                    (ULONG) *(pucResponse + 1)
                    );
              }
           }
           else
           {
              NTStatus = STCtoNT(pucResponse);
           }
        }
     }
  
     return NTStatus;
  }
  
  NTSTATUS
  UsbGetFirmwareRevision(
     PREADER_EXTENSION ReaderExtension)
  /*++
  Description:
  
  Arguments:
  
  Return Value:
  
  --*/
  {
     NTSTATUS NTStatus = STATUS_SUCCESS;
     UCHAR       ucCmd[1];
     UCHAR    ucResponse[4];
  
     ucCmd[0] = 0xE1;
     NTStatus = UsbWrite( ReaderExtension, ucCmd, 2 );
  
     if( NTStatus == STATUS_SUCCESS )
     {
        ReaderExtension->ReadTimeout = 1000;
        NTStatus = UsbRead( ReaderExtension, ucResponse, 4 );
  
        if( NTStatus == STATUS_SUCCESS )
        {
           ReaderExtension->FirmwareMajor = ucResponse[ 2 ];
           ReaderExtension->FirmwareMinor = ucResponse[ 3 ];
        }
     }
     return NTStatus ;
  }
  
  
  NTSTATUS
  UsbRead(
     PREADER_EXTENSION ReaderExtension,
     PUCHAR            pData,
     ULONG          DataLen  )
  /*++
  Description:
     Read data on the USB bus
  
  Arguments:
     ReaderExtension   context of call
     pData       ptr to data buffer
     DataLen        length of data buffer
     pNBytes        number of bytes returned
  
  Return Value:
     STATUS_SUCCESS
     STATUS_BUFFER_TOO_SMALL
     STATUS_UNSUCCESSFUL
  
  --*/
  {
     NTSTATUS NtStatus = STATUS_SUCCESS;
     PURB pUrb;
     USBD_INTERFACE_INFORMATION* pInterfaceInfo;
     USBD_PIPE_INFORMATION* pPipeInfo;
     PDEVICE_OBJECT DeviceObject = ReaderExtension->DeviceObject;
     PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
     ULONG ulSize;
  
     pInterfaceInfo = DeviceExtension->Interface;
  
     ASSERT(pInterfaceInfo != NULL);
  
     if (pInterfaceInfo == NULL) {
  
        // The device has likely been disconnected during hibernate / stand by
        return STATUS_DEVICE_NOT_CONNECTED;
     }
  
     // Read pipe number is 0 on this device
     pPipeInfo = &( pInterfaceInfo->Pipes[ 0 ] );
  
     ulSize = sizeof( struct _URB_BULK_OR_INTERRUPT_TRANSFER );
     pUrb = ReaderExtension->pUrb;
  
  
     UsbBuildInterruptOrBulkTransferRequest(
        pUrb,
        (USHORT)ulSize,
        pPipeInfo->PipeHandle,
        pData,
        NULL,
        DataLen,
        USBD_SHORT_TRANSFER_OK,
        NULL);
  
        NtStatus = UsbCallUSBD( DeviceObject, pUrb );
  
     return NtStatus;
  }
  
  NTSTATUS
  UsbWrite(
     PREADER_EXTENSION ReaderExtension,
     PUCHAR            pData,
     ULONG          DataLen)
  /*++
  Description:
     Write data on the usb port
  
  Arguments:
     ReaderExtension   context of call
     pData          ptr to data buffer
     DataLen           length of data buffer (exclusive LRC!)
  
  Return Value:
     return value of
  
  --*/
  {
     NTSTATUS NtStatus = STATUS_SUCCESS;
     PURB pUrb;
     USBD_INTERFACE_INFORMATION* pInterfaceInfo;
     USBD_PIPE_INFORMATION* pPipeInfo;
     PDEVICE_OBJECT DeviceObject = ReaderExtension->DeviceObject;
     PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
     ULONG ulSize;
  
     pInterfaceInfo = DeviceExtension->Interface;
  
     ASSERT(pInterfaceInfo != NULL);
  
     if (pInterfaceInfo == NULL) {
  
        // The device has likely been disconnected during hibernate / stand by
        return STATUS_DEVICE_NOT_CONNECTED;
     }
  
     // Write pipe number is 1 on this device
     pPipeInfo = &( pInterfaceInfo->Pipes[ 1 ] );
  
     ulSize = sizeof( struct _URB_BULK_OR_INTERRUPT_TRANSFER );
     pUrb = ReaderExtension->pUrb;
  
     UsbBuildInterruptOrBulkTransferRequest(
        pUrb,
        (USHORT)ulSize,
        pPipeInfo->PipeHandle,
        pData,
        NULL,
        DataLen,
        USBD_SHORT_TRANSFER_OK,
        NULL );
  
     NtStatus = UsbCallUSBD( DeviceObject, pUrb );
     
     return NtStatus;
  }
  