共用方式為


從 WinUSB 傳統型應用程式傳送 USB 時序傳輸

從 Windows 8.1 開始,WinUSB Functions 集合具有 API,可讓傳統型應用程式在 USB 裝置的連續端點往返傳輸數據。 針對這類應用程式,Microsoft 提供的 Winusb.sys 必須是設備驅動器。

本文提供下列資訊:

  • 實時傳輸的簡短概觀。
  • 根據端點間隔值傳送緩衝區計算。
  • 使用 WinUSB Functions 傳送讀取和寫入等時序數據的傳輸。

重要 API

從 Windows 8.1 開始,WinUSB Functions 集合具有 API,可讓傳統型應用程式在 USB 裝置的不時點來回傳輸數據。 針對這類應用程式,Microsoft 提供的 Winusb.sys 必須是設備驅動器。

USB 裝置可以支援時序端點,以穩定速率傳輸時間相依的數據,例如音訊/視訊串流。 沒有保證的傳遞。 良好的連線不應卸除任何封包、不正常或預期會遺失封包,但等時通訊協議無法承受這類損失。

主控制器會在總線的保留期間傳送或接收數據,稱為 總線間隔。 總線間隔的單位取決於公交車速度。 針對完整速度,它是 1 毫秒的畫面格,針對高速和 SuperSpeed,其為 250 微秒的微框架。

主機控制器會定期輪詢裝置。 針對讀取作業,當端點準備好傳送數據時,裝置會以總線間隔傳送數據來回應。 若要寫入裝置,主機控制器會傳送數據。

應用程式可以透過一個服務間隔傳送多少數據

本主題中的 等時封包 一詞是指以一個服務間隔傳輸的數據量。 該值是由 USB 驅動程式堆疊計算,而應用程式可以在查詢管道屬性時取得值。

等時封包的大小會決定應用程式配置之傳輸緩衝區的大小。 緩衝區必須結束於框架界限。 傳輸的總大小取決於應用程式想要傳送或接收的數據量。 應用程式起始傳輸之後,主機會將傳輸緩衝區封包化,以便在每個間隔中傳送或接收每個間隔允許的最大位元組。

針對數據傳輸,不會使用所有總線間隔。 在本主題中,所使用的總線 間隔稱為服務間隔

如何計算傳輸數據的框架

應用程式可以選擇使用下列兩種方式之一來指定框架:

  • 自動。 在此模式中,應用程式會指示USB驅動程式堆疊在下一個適當的畫面格中傳送傳輸。 應用程式也必須指定緩衝區是否為連續數據流,讓驅動程式堆疊可以計算開始畫面。
  • 指定晚於目前框架的開始畫面。 應用程式應該考慮應用程式啟動傳輸的時間和 USB 驅動程式堆疊處理它之間的延遲。

程式代碼範例討論

本主題中的範例示範如何使用下列 WinUSB 函式

在本主題中,我們將以三種傳輸方式讀取和寫入 30 毫秒的數據到高速裝置。 管道能夠在每個服務間隔中傳輸 1024 個字節。 因為輪詢間隔是 1,所以數據會在框架的每個微框架中傳送。 總計30個畫面格將包含30*8*1024個字節。

用於傳送讀取和寫入傳輸的函式呼叫類似。 應用程式會配置足以容納這三個傳輸的傳輸緩衝區。 應用程式會呼叫 WinUsb_RegisterIsochBuffer,以註冊特定管道的緩衝區。 呼叫會傳回用來傳送轉移的註冊句柄。 系統會針對後續傳輸重複使用緩衝區,並調整緩衝區中的位移來傳送或接收下一組數據。

範例中的所有傳輸都會以異步方式傳送。 為此,應用程式會配置 一個重迭 結構的數位,其中包含三個元素,每個傳輸各一個。 應用程式會提供事件,以便在傳輸完成並擷取作業結果時收到通知。 因此,在數位中的每個 OVERLAPPED 結構中,應用程式會配置事件,並在 hEvent 成員中設定句柄。

此影像顯示使用 WinUsb_ReadIsochPipeAsap 函式的三 讀取傳輸。 呼叫會指定每個傳輸的位移和長度。 ContinueStream 參數值為 FALSE,表示新的數據流。 之後,應用程式要求後續傳輸會緊接在先前要求的最後一個框架之後排程,以允許連續串流數據。 每一畫面格的封包數目會計算為每個畫面的封包數目 * 畫面數;8*10. 針對此呼叫,應用程式不需要擔心計算開始畫面編號。

用於連續讀取傳輸的 winusb 函式。

此影像顯示使用 WinUsb_WriteIsochPipe 函式的三個寫入傳輸。 呼叫會指定每個傳輸的位移和長度。 在此情況下,應用程式必須計算主機控制器開始傳送數據的框架編號。 在輸出時,函式會接收上一個傳輸中最後一個畫面格之後的畫面格編號。 若要取得目前的框架,應用程式會 呼叫 WinUsb_GetCurrentFrameNumber。 此時,應用程式必須確定下一個傳輸的開始框架晚於目前的畫面格,讓USB驅動程式堆疊不會捨棄晚期封包。 若要這樣做,應用程式會呼叫 WinUsb_GetAdjustedFrameNumber ,以取得實際目前的畫面編號, (此號碼晚於收到的目前畫面編號) 。 若要在安全端,應用程式會新增五個以上的畫面格,然後傳送傳輸。

用於不時時寫入傳輸的 winusb 函式。

每次傳輸完成之後,應用程式會藉由呼叫 WinUsb_GetOverlappedResult來取得傳輸的結果。 bWait 參數設定為 TRUE,因此呼叫在作業完成之前不會傳回。 對於讀取和寫入傳輸, lpNumberOfBytesTransferred 參數一律為 0。 針對寫入傳輸,應用程式會假設作業順利完成,則會傳輸所有位元組。 針對讀取傳輸,每個時序封包的 Length 成員 (USBD_ISO_PACKET_DESCRIPTOR) 包含每個間隔傳輸在該封包中的位元元數目。 若要取得總長度,應用程式會新增所有 Length 值。

完成時,應用程式會藉由呼叫 WinUsb_UnregisterIsochBuffer 來釋放等時序緩衝區句柄。

開始之前

請確定

  • 設備驅動器是 Microsoft 提供的驅動程式:WinUSB (Winusb.sys) 。 該驅動程式包含在 \Windows\System32\ 資料夾中。 如需詳細資訊,請參閱 WinUSB (Winusb.sys) 安裝

  • 您先前已藉由呼叫 WinUsb_Initialize 來取得裝置的 WinUSB 介面句柄。 所有作業都是使用該句柄來執行。 請參閱 如何使用 WinUSB 函式存取 USB 裝置

  • 使用中的介面設定具有等時端點。 否則,您無法存取目標端點的管道。

步驟 1:在作用中設定中尋找等時線

  1. 呼叫 WinUsb_QueryInterfaceSettings,以取得具有等時端點的 USB 介面。
  2. 列舉定義端點之介面設定的管道。
  3. 針對每個端點,呼叫 WinUsb_QueryPipeEx,以取得WINUSB_PIPE_INFORMATION_EX結構中的相關聯管道屬性。 擷取 的 WINUSB_PIPE_INFORMATION_EX 結構,其中包含有關等時線的資訊。 結構包含管道的相關信息、其類型、標識碼等等。
  4. 檢查結構成員,以判斷它是否為必須用於傳輸的管道。 如果是,請儲存 PipeId 值。 在範本程式代碼中,將成員新增至 Device.h 中定義的 DEVICE_DATA 結構。

此範例示範如何判斷使用中設定是否具有時序端點,並取得其相關信息。 在此範例中,裝置是 SuperMUTT 裝置。 裝置在預設介面中有兩個等時點,替代設定 1。


typedef struct _DEVICE_DATA {

    BOOL                    HandlesOpen;
    WINUSB_INTERFACE_HANDLE WinusbHandle;
    HANDLE                  DeviceHandle;
    TCHAR                   DevicePath[MAX_PATH];
    UCHAR                   IsochOutPipe;
    UCHAR                   IsochInPipe;

} DEVICE_DATA, *PDEVICE_DATA;

HRESULT
       GetIsochPipes(
       _Inout_ PDEVICE_DATA DeviceData
       )
{
       BOOL result;
       USB_INTERFACE_DESCRIPTOR usbInterface;
       WINUSB_PIPE_INFORMATION_EX pipe;
       HRESULT hr = S_OK;
       UCHAR i;

       result = WinUsb_QueryInterfaceSettings(DeviceData->WinusbHandle,
              0,
              &usbInterface);

       if (result == FALSE)
       {
              hr = HRESULT_FROM_WIN32(GetLastError());
              printf(_T("WinUsb_QueryInterfaceSettings failed to get USB interface.\n"));
              CloseHandle(DeviceData->DeviceHandle);
              return hr;
       }

       for (i = 0; i < usbInterface.bNumEndpoints; i++)
       {
              result = WinUsb_QueryPipeEx(
                     DeviceData->WinusbHandle,
                     1,
                     (UCHAR) i,
                     &pipe);

              if (result == FALSE)
              {
                     hr = HRESULT_FROM_WIN32(GetLastError());
                     printf(_T("WinUsb_QueryPipeEx failed to get USB pipe.\n"));
                     CloseHandle(DeviceData->DeviceHandle);
                     return hr;
              }

              if ((pipe.PipeType == UsbdPipeTypeIsochronous) && (!(pipe.PipeId == 0x80)))
              {
                     DeviceData->IsochOutPipe = pipe.PipeId;
              }
              else if (pipe.PipeType == UsbdPipeTypeIsochronous)
              {
                     DeviceData->IsochInPipe = pipe.PipeId;
              }
       }

       return hr;
}

SuperMUTT 裝置會在設定 1 時,於預設介面中定義其等時序端點。 上述程式代碼會取得 PipeId 值,並將其儲存在DEVICE_DATA結構中。

步驟 2:取得有關等時線的間隔資訊

接下來,取得您在呼叫 WinUsb_QueryPipeEx 時取得 管道的詳細資訊。

  • 傳輸大小

    1. 擷取的 WINUSB_PIPE_INFORMATION_EX 結構中,取得 MaximumBytesPerIntervalInterval 值。

    2. 根據您想要傳送或接收的等時數據量,計算傳輸大小。 例如,請考慮此計算:

      TransferSize = ISOCH_DATA_SIZE_MS * pipeInfoEx.MaximumBytesPerInterval * (8 / pipeInfoEx.Interval);

      在此範例中,傳輸大小會計算為10毫秒的等時數據。

  • 時序封包數目例如,請考慮此計算:

    若要計算保存整個傳輸所需的等時封包總數。 讀取傳輸需要此資訊,並計算為 >IsochInTransferSize / pipe.MaximumBytesPerInterval;

此範例示範將程式代碼新增至步驟 1 範例,並取得不時針管道的間隔值。


#define ISOCH_DATA_SIZE_MS   10

typedef struct _DEVICE_DATA {

    BOOL                    HandlesOpen;
    WINUSB_INTERFACE_HANDLE WinusbHandle;
    HANDLE                  DeviceHandle;
    TCHAR                   DevicePath[MAX_PATH];
                UCHAR                   IsochOutPipe;
                UCHAR                   IsochInPipe;
                ULONG                   IsochInTransferSize;
                ULONG                   IsochOutTransferSize;
                ULONG                   IsochInPacketCount;

} DEVICE_DATA, *PDEVICE_DATA;


...

if ((pipe.PipeType == UsbdPipeTypeIsochronous) && (!(pipe.PipeId == 0x80)))
{
       DeviceData->IsochOutPipe = pipe.PipeId;

       if ((pipe.MaximumBytesPerInterval == 0) || (pipe.Interval == 0))
       {
         hr = E_INVALIDARG;
             printf("Isoch Out: MaximumBytesPerInterval or Interval value is 0.\n");
             CloseHandle(DeviceData->DeviceHandle);
             return hr;
       }
       else
       {
             DeviceData->IsochOutTransferSize =
                 ISOCH_DATA_SIZE_MS *
                 pipe.MaximumBytesPerInterval *
                 (8 / pipe.Interval);
       }
}
else if (pipe.PipeType == UsbdPipeTypeIsochronous)
{
       DeviceData->IsochInPipe = pipe.PipeId;

       if (pipe.MaximumBytesPerInterval == 0 || (pipe.Interval == 0))
       {
         hr = E_INVALIDARG;
             printf("Isoch Out: MaximumBytesPerInterval or Interval value is 0.\n");
             CloseHandle(DeviceData->DeviceHandle);
             return hr;
       }
       else
       {
             DeviceData->IsochInTransferSize =
                 ISOCH_DATA_SIZE_MS *
                 pipe.MaximumBytesPerInterval *
                 (8 / pipe.Interval);

             DeviceData->IsochInPacketCount =
                  DeviceData->IsochInTransferSize / pipe.MaximumBytesPerInterval;
       }
}

...

在上述程式代碼中,應用程式會從 WINUSB_PIPE_INFORMATION_EX 取得 IntervalMaximumBytesPerInterval,以計算讀取傳輸所需的傳輸大小和時序封包數目。 針對這兩個時序端點, 間隔 為 1。 該值表示框架的所有微框架都會攜帶數據。 根據此情況,若要傳送 10 毫秒的數據,您需要 10 個畫面格,傳輸大小總計為 10*1024*8 個字節,而 80 個時序封包,每 1024 個字節長。

步驟 3:傳送寫入傳輸以將數據傳送至連續 OUT 端點

此程式摘要說明將數據寫入到不時點的步驟。

  1. 配置包含要傳送之數據的緩衝區。
  2. 如果您要以異步方式傳送數據,請配置並初始化包含呼叫端配置事件物件的句柄的 OVERLAPPED 結構。 結構必須初始化為零,否則呼叫會失敗。
  3. 呼叫 WinUsb_RegisterIsochBuffer 來註冊緩衝區。
  4. 呼叫 WinUsb_WriteIsochPipeAsap 以啟動傳輸。 如果您想要手動指定傳輸數據的框架,請改為呼叫 WinUsb_WriteIsochPipe
  5. 呼叫 WinUsb_GetOverlappedResult 以取得傳輸的結果。
  6. 完成時,呼叫 WinUsb_UnregisterIsochBuffer、重疊的事件句柄和傳輸緩衝區,以釋放緩衝區句柄。

以下是示範如何傳送寫入傳輸的範例。

#define ISOCH_TRANSFER_COUNT   3

VOID
    SendIsochOutTransfer(
    _Inout_ PDEVICE_DATA DeviceData,
    _In_ BOOL AsapTransfer
    )
{
    PUCHAR writeBuffer;
    LPOVERLAPPED overlapped;
    ULONG numBytes;
    BOOL result;
    DWORD lastError;
    WINUSB_ISOCH_BUFFER_HANDLE isochWriteBufferHandle;
    ULONG frameNumber;
    ULONG startFrame;
    LARGE_INTEGER timeStamp;
    ULONG i;
    ULONG totalTransferSize;

    isochWriteBufferHandle = INVALID_HANDLE_VALUE;
    writeBuffer = NULL;
    overlapped = NULL;

    printf(_T("\n\nWrite transfer.\n"));

    totalTransferSize = DeviceData->IsochOutTransferSize * ISOCH_TRANSFER_COUNT;

    if (totalTransferSize % DeviceData->IsochOutBytesPerFrame != 0)
    {
        printf(_T("Transfer size must end at a frame boundary.\n"));
        goto Error;
    }

    writeBuffer = new UCHAR[totalTransferSize];

    if (writeBuffer == NULL)
    {
        printf(_T("Unable to allocate memory.\n"));
        goto Error;
    }

    ZeroMemory(writeBuffer, totalTransferSize);

    overlapped = new OVERLAPPED[ISOCH_TRANSFER_COUNT];
    if (overlapped == NULL)
    {
        printf("Unable to allocate memory.\n");
        goto Error;

    }

    ZeroMemory(overlapped, (sizeof(OVERLAPPED) * ISOCH_TRANSFER_COUNT));

    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
    {
        overlapped[i].hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

        if (overlapped[i].hEvent == NULL)
        {
            printf("Unable to set event for overlapped operation.\n");
            goto Error;

        }
    }

    result = WinUsb_RegisterIsochBuffer(
        DeviceData->WinusbHandle,
        DeviceData->IsochOutPipe,
        writeBuffer,
        totalTransferSize,
        &isochWriteBufferHandle);

    if (!result)
    {
        printf(_T("Isoch buffer registration failed.\n"));
        goto Error;
    }

    result = WinUsb_GetCurrentFrameNumber(
                DeviceData->WinusbHandle,
                &frameNumber,
                &timeStamp);

    if (!result)
    {
        printf(_T("WinUsb_GetCurrentFrameNumber failed.\n"));
        goto Error;
    }

    startFrame = frameNumber + 5;

    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
    {

        if (AsapTransfer)
        {
            result = WinUsb_WriteIsochPipeAsap(
                isochWriteBufferHandle,
                DeviceData->IsochOutTransferSize * i,
                DeviceData->IsochOutTransferSize,
                (i == 0) ? FALSE : TRUE,
                &overlapped[i]);

            printf(_T("Write transfer sent by using ASAP flag.\n"));
        }
        else
        {

            printf("Transfer starting at frame %d.\n", startFrame);

            result = WinUsb_WriteIsochPipe(
                isochWriteBufferHandle,
                i * DeviceData->IsochOutTransferSize,
                DeviceData->IsochOutTransferSize,
                &startFrame,
                &overlapped[i]);

            printf("Next transfer frame %d.\n", startFrame);

        }

        if (!result)
        {
            lastError = GetLastError();

            if (lastError != ERROR_IO_PENDING)
            {
                printf("Failed to send write transfer with error %x\n", lastError);
            }
        }
    }

    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
    {
        result = WinUsb_GetOverlappedResult(
            DeviceData->WinusbHandle,
            &overlapped[i],
            &numBytes,
            TRUE);

        if (!result)
        {
            lastError = GetLastError();

            printf("Write transfer %d with error %x\n", i, lastError);
        }
        else
        {
            printf("Write transfer %d completed. \n", i);

        }
    }

Error:
    if (isochWriteBufferHandle != INVALID_HANDLE_VALUE)
    {
        result = WinUsb_UnregisterIsochBuffer(isochWriteBufferHandle);
        if (!result)
        {
            printf(_T("Failed to unregister isoch write buffer. \n"));
        }
    }

    if (writeBuffer != NULL)
    {
        delete [] writeBuffer;
    }

    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
    {
        if (overlapped[i].hEvent != NULL)
        {
            CloseHandle(overlapped[i].hEvent);
        }

    }

    if (overlapped != NULL)
    {
        delete [] overlapped;
    }

    return;
}

步驟 4:傳送讀取傳輸以接收來自不時點 IN 端點的數據

此程式摘要說明從不時點讀取數據的步驟。

  1. 配置傳輸緩衝區,以在傳輸結束時接收數據。 緩衝區的大小必須以步驟 1 中的傳輸大小計算為基礎。 傳輸緩衝區必須結束於框架界限。
  2. 如果您要以異步方式傳送數據,請配置包含呼叫端配置事件物件的句柄的 OVERLAPPED 結構。 結構必須初始化為零,否則呼叫會失敗。
  3. 呼叫 WinUsb_RegisterIsochBuffer 來註冊緩衝區。
  4. 根據步驟 2 中計算的時序封包數目,在USBD_ISO_PACKET_DESCRIPTOR) (配置等時序封包陣列。
  5. 呼叫 WinUsb_ReadIsochPipeAsap 來啟動轉移。 如果您想要手動指定要傳送數據的開始畫面,請改為呼叫 WinUsb_ReadIsochPipe
  6. 呼叫 WinUsb_GetOverlappedResult 以取得傳輸的結果。
  7. 完成時,藉由呼叫 WinUsb_UnregisterIsochBuffer、重疊的事件句柄、時序封包陣列和傳輸緩衝區來釋放緩衝區句柄。

以下是示範如何藉由呼叫 WinUsb_ReadIsochPipeAsap 和 WinUsb_ReadIsochPipe 來傳送讀取傳輸的範例。

#define ISOCH_TRANSFER_COUNT   3

VOID
    SendIsochInTransfer(
    _Inout_ PDEVICE_DATA DeviceData,
    _In_ BOOL AsapTransfer
    )
{
    PUCHAR readBuffer;
    LPOVERLAPPED overlapped;
    ULONG numBytes;
    BOOL result;
    DWORD lastError;
    WINUSB_ISOCH_BUFFER_HANDLE isochReadBufferHandle;
    PUSBD_ISO_PACKET_DESCRIPTOR isochPackets;
    ULONG i;
    ULONG j;

    ULONG frameNumber;
    ULONG startFrame;
    LARGE_INTEGER timeStamp;

    ULONG totalTransferSize;

    readBuffer = NULL;
    isochPackets = NULL;
    overlapped = NULL;
    isochReadBufferHandle = INVALID_HANDLE_VALUE;

    printf(_T("\n\nRead transfer.\n"));

    totalTransferSize = DeviceData->IsochOutTransferSize * ISOCH_TRANSFER_COUNT;

    if (totalTransferSize % DeviceData->IsochOutBytesPerFrame != 0)
    {
        printf(_T("Transfer size must end at a frame boundary.\n"));
        goto Error;
    }

    readBuffer = new UCHAR[totalTransferSize];

    if (readBuffer == NULL)
    {
        printf(_T("Unable to allocate memory.\n"));
        goto Error;
    }

    ZeroMemory(readBuffer, totalTransferSize);

    overlapped = new OVERLAPPED[ISOCH_TRANSFER_COUNT];
    ZeroMemory(overlapped, (sizeof(OVERLAPPED) * ISOCH_TRANSFER_COUNT));

    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
    {
        overlapped[i].hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

        if (overlapped[i].hEvent == NULL)
        {
            printf("Unable to set event for overlapped operation.\n");
            goto Error;
        }
    }

    isochPackets = new USBD_ISO_PACKET_DESCRIPTOR[DeviceData->IsochInPacketCount * ISOCH_TRANSFER_COUNT];
    ZeroMemory(isochPackets, DeviceData->IsochInPacketCount * ISOCH_TRANSFER_COUNT);

    result = WinUsb_RegisterIsochBuffer(
        DeviceData->WinusbHandle,
        DeviceData->IsochInPipe,
        readBuffer,
        DeviceData->IsochInTransferSize * ISOCH_TRANSFER_COUNT,
        &isochReadBufferHandle);

    if (!result)
    {
        printf(_T("Isoch buffer registration failed.\n"));
        goto Error;
    }

    result = WinUsb_GetCurrentFrameNumber(
                DeviceData->WinusbHandle,
                &frameNumber,
                &timeStamp);

    if (!result)
    {
        printf(_T("WinUsb_GetCurrentFrameNumber failed.\n"));
        goto Error;
    }

    startFrame = frameNumber + 5;

    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
    {
        if (AsapTransfer)
        {
            result = WinUsb_ReadIsochPipeAsap(
                isochReadBufferHandle,
                DeviceData->IsochInTransferSize * i,
                DeviceData->IsochInTransferSize,
                (i == 0) ? FALSE : TRUE,
                DeviceData->IsochInPacketCount,
                &isochPackets[i * DeviceData->IsochInPacketCount],
                &overlapped[i]);

            printf(_T("Read transfer sent by using ASAP flag.\n"));
        }
        else
        {
            printf("Transfer starting at frame %d.\n", startFrame);

            result = WinUsb_ReadIsochPipe(
                isochReadBufferHandle,
                DeviceData->IsochInTransferSize * i,
                DeviceData->IsochInTransferSize,
                &startFrame,
                DeviceData->IsochInPacketCount,
                &isochPackets[i * DeviceData->IsochInPacketCount],
                &overlapped[i]);

            printf("Next transfer frame %d.\n", startFrame);
        }

        if (!result)
        {
            lastError = GetLastError();

            if (lastError != ERROR_IO_PENDING)
            {
                printf("Failed to start a read operation with error %x\n", lastError);
            }
        }
    }

    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
    {
        result = WinUsb_GetOverlappedResult(
            DeviceData->WinusbHandle,
            &overlapped[i],
            &numBytes,
            TRUE);

        if (!result)
        {
            lastError = GetLastError();

            printf("Failed to read with error %x\n", lastError);
        }
        else
        {
            numBytes = 0;
            for (j = 0; j < DeviceData->IsochInPacketCount; j++)
            {
                numBytes += isochPackets[j].Length;
            }

            printf("Requested %d bytes in %d packets per transfer.\n", DeviceData->IsochInTransferSize, DeviceData->IsochInPacketCount);
        }

        printf("Transfer %d completed. Read %d bytes. \n\n", i+1, numBytes);
    }

Error:
    if (isochReadBufferHandle != INVALID_HANDLE_VALUE)
    {
        result = WinUsb_UnregisterIsochBuffer(isochReadBufferHandle);
        if (!result)
        {
            printf(_T("Failed to unregister isoch read buffer. \n"));
        }
    }

    if (readBuffer != NULL)
    {
        delete [] readBuffer;
    }

    if (isochPackets != NULL)
    {
        delete [] isochPackets;
    }

    for (i = 0; i < ISOCH_TRANSFER_COUNT; i++)
    {
        if (overlapped[i].hEvent != NULL)
        {
            CloseHandle(overlapped[i].hEvent);
        }
    }

    if (overlapped != NULL)
    {
        delete [] overlapped;
    }
    return;
}