Mengirim transfer isochronous USB dari aplikasi desktop WinUSB

Mulai dari Windows 8.1, kumpulan Fungsi WinUSB memiliki API yang memungkinkan aplikasi desktop untuk mentransfer data ke dan dari titik akhir isochronous perangkat USB. Untuk aplikasi seperti itu, Winusb.sys yang disediakan Microsoft harus menjadi driver perangkat.

Artikel ini memberikan informasi berikut:

  • Gambaran umum singkat transfer isochronous.
  • Mentransfer perhitungan buffer berdasarkan nilai interval titik akhir.
  • Mengirim transfer yang membaca dan menulis data isochronous dengan menggunakan Fungsi WinUSB.

API penting

Mulai dari Windows 8.1, kumpulan Fungsi WinUSB memiliki API yang memungkinkan aplikasi desktop untuk mentransfer data ke dan dari titik akhir isochronous perangkat USB. Untuk aplikasi seperti itu, Winusb.sys yang disediakan Microsoft harus menjadi driver perangkat.

Perangkat USB dapat mendukung titik akhir isochronous untuk mentransfer data yang bergantung pada waktu dengan kecepatan stabil, seperti dengan streaming audio/video. Tidak ada pengiriman yang dijamin. Koneksi yang baik tidak boleh menghilangkan paket apa pun, itu tidak normal atau diharapkan kehilangan paket, tetapi protokol isochronous toleran terhadap kerugian tersebut.

Pengontrol host mengirim atau menerima data selama periode waktu yang dipesan di bus, disebut interval bus. Unit interval bus tergantung pada kecepatan bus. Untuk kecepatan penuh, ini adalah bingkai 1 milidetik, untuk kecepatan tinggi dan SuperSpeed, ini adalah mikroframe 250 mikro detik.

Pengontrol host melakukan polling perangkat secara berkala. Untuk operasi baca, ketika titik akhir siap mengirim data, perangkat merespons dengan mengirim data dalam interval bus. Untuk menulis ke perangkat, pengontrol host mengirim data.

Berapa banyak data yang dapat dikirim aplikasi dalam satu interval layanan

Istilah paket isochronous dalam topik ini mengacu pada jumlah data yang ditransfer dalam satu interval layanan. Nilai tersebut dihitung oleh tumpukan driver USB dan aplikasi bisa mendapatkan nilai saat mengkueri atribut pipa.

Ukuran paket isochronous menentukan ukuran buffer transfer yang dialokasikan aplikasi. Buffer harus berakhir pada batas bingkai. Ukuran total transfer tergantung pada berapa banyak data yang ingin dikirim atau diterima aplikasi. Setelah transfer dimulai oleh aplikasi, host mengemas buffer transfer sehingga dalam setiap interval, host dapat mengirim atau menerima byte maksimum yang diizinkan per interval.

Untuk transfer data, tidak semua interval bus digunakan. Dalam topik ini, interval bus yang digunakan disebut interval layanan.

Cara menghitung bingkai di mana data ditransmisikan

Aplikasi ini dapat memilih untuk menentukan bingkai dengan salah satu dari dua cara:

  • Secara otomatis. Dalam mode ini, aplikasi menginstruksikan tumpukan driver USB untuk mengirim transfer dalam bingkai yang sesuai berikutnya. Aplikasi juga harus menentukan apakah buffer adalah aliran berkelanjutan sehingga tumpukan driver dapat menghitung bingkai mulai.
  • Menentukan bingkai awal yang lebih baru dari bingkai saat ini. Aplikasi harus mempertimbangkan latensi antara waktu aplikasi memulai transfer dan kapan tumpukan driver USB memprosesnya.

Contoh diskusi kode

Contoh dalam topik ini menunjukkan penggunaan Fungsi WinUSB ini:

Dalam topik ini, kita akan membaca dan menulis 30 milidetik data dalam tiga transfer ke perangkat berkecepatan tinggi. Pipa ini mampu mentransfer 1024 byte di setiap interval layanan. Karena interval polling adalah 1, data ditransfer di setiap mikroframe bingkai. Total 30 frame akan membawa 30 * 8 * 1024 byte.

Panggilan fungsi untuk mengirim transfer baca dan tulis serupa. Aplikasi ini mengalokasikan buffer transfer yang cukup besar untuk menahan ketiga transfer. Aplikasi ini mendaftarkan buffer untuk pipa tertentu dengan memanggil WinUsb_RegisterIsochBuffer. Panggilan mengembalikan handel pendaftaran yang digunakan untuk mengirim transfer. Buffer digunakan kembali untuk transfer dan offset berikutnya di buffer disesuaikan untuk mengirim atau menerima kumpulan data berikutnya.

Semua transfer dalam contoh dikirim secara asinkron. Untuk ini, aplikasi mengalokasikan array struktur TUMPANG TINDIH dengan tiga elemen, satu untuk setiap transfer. Aplikasi ini menyediakan peristiwa sehingga dapat diberi tahu saat transfer selesai dan mengambil hasil operasi. Untuk ini, di setiap struktur yang TUMPANG TINDIH dalam array, aplikasi mengalokasikan peristiwa dan mengatur handel di anggota hEvent .

Gambar ini menunjukkan tiga transfer baca dengan menggunakan fungsi WinUsb_ReadIsochPipeAsap . Panggilan menentukan offset dan panjang setiap transfer. Nilai parameter ContinueStream adalah FALSE untuk menunjukkan aliran baru. Setelah itu, aplikasi meminta agar transfer berikutnya dijadwalkan segera mengikuti bingkai terakhir permintaan sebelumnya untuk memungkinkan streaming data berkelanjutan. Jumlah paket isochronous dihitung sebagai paket per bingkai * jumlah bingkai; 8*10. Untuk panggilan ini, aplikasi tidak perlu khawatir tentang menghitung nomor bingkai mulai.

fungsi winusb untuk transfer baca isochronous.

Gambar ini menunjukkan tiga transfer tulis dengan menggunakan fungsi WinUsb_WriteIsochPipe . Panggilan menentukan offset dan panjang setiap transfer. Dalam hal ini, aplikasi harus menghitung nomor bingkai tempat pengontrol host dapat mulai mengirim data. Pada output, fungsi menerima nomor bingkai bingkai yang mengikuti bingkai terakhir yang digunakan dalam transfer sebelumnya. Untuk mendapatkan bingkai saat ini, aplikasi memanggil WinUsb_GetCurrentFrameNumber. Pada titik ini, aplikasi harus memastikan bahwa bingkai awal transfer berikutnya lebih lambat dari bingkai saat ini, sehingga tumpukan driver USB tidak menghilangkan paket terlambat. Untuk melakukannya, aplikasi memanggil WinUsb_GetAdjustedFrameNumber untuk mendapatkan nomor bingkai saat ini yang realistis (ini lebih lambat dari nomor bingkai saat ini yang diterima). Untuk berada di sisi aman, aplikasi menambahkan lima bingkai lagi, lalu mengirim transfer.

fungsi winusb untuk transfer tulis isochronous.

Setelah setiap transfer selesai, aplikasi mendapatkan hasil transfer dengan memanggil WinUsb_GetOverlappedResult. Parameter bWait diatur ke TRUE sehingga panggilan tidak kembali hingga operasi selesai. Untuk transfer baca dan tulis, parameter lpNumberOfBytesTransferred selalu 0. Untuk transfer tulis, aplikasi mengasumsikan bahwa jika operasi berhasil diselesaikan, semua byte ditransfer. Untuk transfer baca, anggota Panjang setiap paket isochronous (USBD_ISO_PACKET_DESCRIPTOR), berisi jumlah byte yang ditransfer dalam paket tersebut, per interval. Untuk mendapatkan panjang total, aplikasi menambahkan semua nilai Panjang .

Setelah selesai, aplikasi merilis handel buffer isochronous dengan memanggil WinUsb_UnregisterIsochBuffer.

Sebelum memulai

Pastikan bahwa,

  • Driver perangkat adalah driver yang disediakan Microsoft: WinUSB (Winusb.sys). Driver tersebut disertakan dalam folder \Windows\System32\. Untuk informasi selengkapnya, lihat Penginstalan WinUSB (Winusb.sys).

  • Anda sebelumnya telah mendapatkan handel antarmuka WinUSB ke perangkat dengan memanggil WinUsb_Initialize. Semua operasi dilakukan dengan menggunakan handel tersebut. Baca Cara Mengakses Perangkat USB dengan Menggunakan Fungsi WinUSB.

  • Pengaturan antarmuka aktif memiliki titik akhir isochronous. Jika tidak, Anda tidak dapat mengakses pipa untuk titik akhir target.

Langkah 1: Temukan pipa isochronous di pengaturan aktif

  1. Dapatkan antarmuka USB yang memiliki titik akhir isochronous dengan memanggil WinUsb_QueryInterfaceSettings.
  2. Menghitung pipa pengaturan antarmuka yang menentukan titik akhir.
  3. Untuk setiap titik akhir, dapatkan properti pipa terkait dalam struktur WINUSB_PIPE_INFORMATION_EX dengan memanggil WinUsb_QueryPipeEx. Struktur WINUSB_PIPE_INFORMATION_EX yang diambil yang berisi informasi tentang pipa isochronous. Struktur berisi informasi tentang pipa, jenisnya, id, dan sebagainya.
  4. Periksa anggota struktur untuk menentukan apakah itu pipa yang harus digunakan untuk transfer. Jika ya, simpan nilai PipeId . Dalam kode templat, tambahkan anggota ke struktur DEVICE_DATA, yang ditentukan dalam Device.h.

Contoh ini menunjukkan cara menentukan apakah pengaturan aktif memiliki titik akhir isochronous dan mendapatkan informasi tentang pengaturan tersebut. Dalam contoh ini perangkat adalah perangkat SuperMUTT. Perangkat ini memiliki dua titik akhir isochronous di antarmuka default, pengaturan alternatif 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;
}

Perangkat SuperMUTT mendefinisikan titik akhir isochronous-nya di antarmuka default, pada pengaturan 1. Kode sebelumnya mendapatkan nilai PipeId dan menyimpannya dalam struktur DEVICE_DATA.

Langkah 2: Dapatkan informasi interval tentang pipa isochronous

Selanjutnya, dapatkan informasi selengkapnya tentang pipa yang Anda peroleh dalam panggilan ke WinUsb_QueryPipeEx.

  • Ukuran transfer

    1. Dari struktur WINUSB_PIPE_INFORMATION_EX yang diambil, dapatkan nilai MaximumBytesPerInterval dan Interval .

    2. Bergantung pada jumlah data isochronous yang ingin Anda kirim atau terima, hitung ukuran transfer. Misalnya, pertimbangkan perhitungan ini:

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

      Dalam contoh, ukuran transfer dihitung selama 10 milidetik data isochronous.

  • Jumlah paket isochronousMisalnya, pertimbangkan perhitungan ini:

    Untuk menghitung jumlah total paket isochronous yang diperlukan untuk menahan seluruh transfer. Informasi ini diperlukan untuk transfer baca dan dihitung sebagai, >IsochInTransferSize / pipe.MaximumBytesPerInterval;.

Contoh ini menunjukkan tambahkan kode ke contoh langkah 1 dan mendapatkan nilai interval untuk pipa isochronous.


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

...

Dalam kode sebelumnya, aplikasi mendapatkan Interval dan MaximumBytesPerInterval dari WINUSB_PIPE_INFORMATION_EX untuk menghitung ukuran transfer dan jumlah paket isochronous yang diperlukan untuk transfer baca. Untuk kedua titik akhir isochronous, Interval adalah 1. Nilai itu menunjukkan bahwa semua mikroframe bingkai membawa data. Berdasarkan itu, untuk mengirim 10 milidetik data, Anda memerlukan 10 bingkai, ukuran transfer total adalah 10 * 1024 * 8 byte dan 80 paket isochronous, masing-masing panjang 1024 byte.

Langkah 3: Kirim transfer tulis untuk mengirim data ke titik akhir OUT isochronous

Prosedur ini merangkum langkah-langkah untuk menulis data ke titik akhir isochronous.

  1. Alokasikan buffer yang berisi data yang akan dikirim.
  2. Jika Anda mengirim data secara asinkron, alokasikan dan inisialisasi struktur TUMPANG TINDIH yang berisi handel ke objek peristiwa yang dialokasikan pemanggil. Struktur harus diinisialisasi ke nol, jika tidak, panggilan gagal.
  3. Daftarkan buffer dengan memanggil WinUsb_RegisterIsochBuffer.
  4. Mulai transfer dengan memanggil WinUsb_WriteIsochPipeAsap. Jika Anda ingin menentukan bingkai di mana data akan ditransfer secara manual, panggil WinUsb_WriteIsochPipe sebagai gantinya.
  5. Dapatkan hasil transfer dengan memanggil WinUsb_GetOverlappedResult.
  6. Setelah selesai, lepaskan handel buffer dengan memanggil WinUsb_UnregisterIsochBuffer, handel peristiwa yang tumpang tindih, dan buffer transfer.

Berikut adalah contoh yang menunjukkan cara mengirim transfer tulis.

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

Langkah 4: Mengirim transfer baca untuk menerima data dari titik akhir IN isochronous

Prosedur ini merangkum langkah-langkah untuk membaca data dari titik akhir isochronous.

  1. Alokasikan buffer transfer yang akan menerima data di akhir transfer. Ukuran buffer harus didasarkan pada ukuran transfer yang dihitung di langkah 1. Buffer transfer harus berakhir pada batas bingkai.
  2. Jika Anda mengirim data secara asinkron, alokasikan struktur TUMPANG TINDIH yang berisi handel ke objek peristiwa yang dialokasikan pemanggil. Struktur harus diinisialisasi ke nol, jika tidak, panggilan gagal.
  3. Daftarkan buffer dengan memanggil WinUsb_RegisterIsochBuffer.
  4. Berdasarkan jumlah paket isochronous yang dihitung pada langkah 2, alokasikan array paket isochronous (USBD_ISO_PACKET_DESCRIPTOR).
  5. Mulai transfer dengan memanggil WinUsb_ReadIsochPipeAsap. Jika Anda ingin menentukan bingkai mulai secara manual tempat data akan ditransfer, panggil WinUsb_ReadIsochPipe sebagai gantinya.
  6. Dapatkan hasil transfer dengan memanggil WinUsb_GetOverlappedResult.
  7. Setelah selesai, lepaskan handel buffer dengan memanggil WinUsb_UnregisterIsochBuffer, handel peristiwa yang tumpang tindih, array paket isochronous, dan buffer transfer.

Berikut adalah contoh yang menunjukkan cara mengirim transfer baca dengan memanggil WinUsb_ReadIsochPipeAsap dan 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;
}