Cara mengirim permintaan transfer massal USB

Topik ini memberikan gambaran singkat tentang transfer massal USB. Ini juga memberikan instruksi langkah demi langkah tentang bagaimana driver klien dapat mengirim dan menerima data massal dari perangkat.

Tentang titik akhir massal

Titik akhir massal USB dapat mentransfer data dalam jumlah besar. Transfer massal dapat diandalkan yang memungkinkan deteksi kesalahan perangkat keras, dan melibatkan percobaan ulang dalam perangkat keras dalam jumlah terbatas. Untuk transfer ke titik akhir massal, bandwidth tidak dicadangkan di bus. Ketika ada beberapa permintaan transfer yang menargetkan berbagai jenis titik akhir, pengontrol terlebih dahulu menjadwalkan transfer untuk data penting waktu, seperti paket isochronous dan interupsi. Hanya jika ada bandwidth yang tidak digunakan yang tersedia di bus, pengontrol menjadwalkan transfer massal. Di mana tidak ada lalu lintas signifikan lainnya di bus, transfer massal bisa cepat. Namun, ketika bus sibuk dengan transfer lain, data massal dapat menunggu tanpa batas waktu.

Berikut adalah fitur utama titik akhir massal:

  • Titik akhir massal bersifat opsional. Mereka didukung oleh perangkat USB yang ingin mentransfer data dalam jumlah besar. Misalnya, mentransfer file ke flash drive, data ke atau dari printer atau pemindai.
  • Perangkat USB berkecepatan penuh, kecepatan tinggi, dan SuperSpeed mendukung titik akhir massal. Perangkat berkecepatan rendah tidak mendukung titik akhir massal.
  • Titik akhir adalah satu arah dan data dapat ditransfer baik dalam arah IN atau OUT. Titik akhir IN massal digunakan untuk membaca data dari perangkat ke host dan titik akhir OUT massal digunakan untuk mengirim data dari host ke perangkat.
  • Titik akhir memiliki bit CRC untuk memeriksa kesalahan dan dengan demikian memberikan integritas data. Untuk kesalahan CRC, data ditransmisikan ulang secara otomatis.
  • Titik akhir massal SuperSpeed dapat mendukung aliran. Aliran memungkinkan host mengirim transfer ke pipa aliran individual.
  • Ukuran paket maksimum titik akhir massal tergantung pada kecepatan bus perangkat. Untuk kecepatan penuh, kecepatan tinggi, dan SuperSpeed; ukuran paket maksimum masing-masing adalah 64, 512, dan 1024 byte.

Transaksi massal

Seperti semua transfer USB lainnya, host selalu memulai transfer massal. Komunikasi terjadi antara host dan titik akhir target. Protokol USB tidak memberlakukan format apa pun pada data yang dikirim dalam transaksi massal.

Bagaimana host dan perangkat berkomunikasi di bus tergantung pada kecepatan perangkat terhubung. Bagian ini menjelaskan beberapa contoh transfer massal kecepatan tinggi dan SuperSpeed yang menunjukkan komunikasi antara host dan perangkat.

Anda dapat melihat struktur transaksi dan paket dengan menggunakan penganalisis USB apa pun, seperti Beagle, Ellisys, penganalisis protokol USB LeCroy. Perangkat penganalisis menunjukkan bagaimana data dikirim ke atau diterima dari perangkat USB melalui kabel. Dalam contoh ini, mari kita periksa beberapa jejak yang ditangkap oleh penganalisis USB LeCroy. Contoh ini hanya untuk informasi. Ini bukan dukungan oleh Microsoft.

Contoh transaksi OUT massal

Jejak penganalisis ini menunjukkan contoh transaksi OUT massal dengan kecepatan tinggi.

Cuplikan layar yang memperlihatkan jejak contoh transaksi penganalisis OUT massal.

Dalam jejak sebelumnya, host memulai transfer KELUAR massal ke titik akhir massal berkecepatan tinggi, dengan mengirim paket token dengan PID diatur ke OUT (token OUT). Paket berisi alamat perangkat dan titik akhir target. Setelah paket OUT, host mengirimkan paket data yang berisi payload massal. Jika titik akhir menerima data masuk, titik akhir akan mengirim paket ACK. Dalam contoh ini, kita dapat melihat bahwa host mengirim 31 byte ke alamat perangkat:1; alamat titik akhir: 2.

Jika titik akhir sibuk pada saat paket data tiba dan tidak dapat menerima data, perangkat dapat mengirim paket NAK. Dalam hal ini, host mulai mengirim paket PING ke perangkat. Perangkat merespons dengan paket NAK selama perangkat tidak siap menerima data. Ketika perangkat siap, perangkat merespons dengan paket ACK. Host kemudian dapat melanjutkan transfer OUT.

Jejak penganalisis ini menunjukkan contoh transaksi OUT massal SuperSpeed.

Cuplikan layar yang memperlihatkan jejak contoh transaksi data MASSAL SuperSpeed.

Dalam jejak sebelumnya, host memulai transaksi OUT ke titik akhir massal SuperSpeed dengan mengirim paket data. Paket data berisi payload massal, perangkat, dan alamat titik akhir. Dalam contoh ini, kita dapat melihat bahwa host mengirim 31 byte ke alamat perangkat:4; alamat titik akhir: 2.

Perangkat menerima dan mengakui paket data dan mengirim paket ACK kembali ke host. Jika titik akhir sibuk pada saat paket data tiba dan tidak dapat menerima data, perangkat dapat mengirim paket NRDY. Tidak seperti kecepatan tinggi, setelah menerima paket NRDY, host tidak berulang kali melakukan polling pada perangkat. Sebaliknya, host menunggu ERDY dari perangkat. Ketika perangkat siap, perangkat mengirim paket ERDY dan host kemudian dapat mengirim data ke titik akhir.

Contoh transaksi IN massal

Jejak penganalisis ini menunjukkan contoh transaksi IN massal dengan kecepatan tinggi.

Cuplikan layar yang memperlihatkan jejak contoh transaksi data IN massal.

Dalam jejak sebelumnya, host memulai transaksi dengan mengirim paket token dengan PID yang diatur ke IN (token IN). Perangkat kemudian mengirim paket data dengan payload massal. Jika titik akhir tidak memiliki data untuk dikirim atau belum siap untuk mengirim data, perangkat dapat mengirim paket jabat tangan NAK. Host mencoba kembali transfer IN hingga menerima paket ACK dari perangkat. Paket ACK itu menyiratkan bahwa perangkat telah menerima data.

Jejak penganalisis ini menunjukkan contoh transaksi IN massal SuperSpeed.

jejak contoh transaksi data.

Untuk memulai transfer IN massal dari titik akhir SuperSpeed, host memulai transaksi massal dengan mengirim paket ACK. Spesifikasi USB versi 3.0 mengoptimalkan bagian awal transfer ini dengan menggabungkan paket ACK dan IN ke dalam satu paket ACK. Alih-alih token IN, untuk SuperSpeed, host mengirimkan token ACK untuk memulai transfer massal. Perangkat merespons dengan paket data. Host kemudian mengakui paket data dengan mengirim paket ACK. Jika titik akhir sibuk dan tidak dapat mengirim data, perangkat dapat mengirim status NRDY. Dalam hal ini, host menunggu sampai mendapatkan paket ERDY dari perangkat.

Tugas driver klien USB untuk transfer massal

Aplikasi atau driver pada host selalu memulai transfer massal untuk mengirim atau menerima data. Driver klien mengirimkan permintaan ke tumpukan driver USB. Tumpukan driver USB memprogram permintaan ke pengontrol host dan kemudian mengirim paket protokol (seperti yang dijelaskan di bagian sebelumnya) melalui kawat ke perangkat.

Mari kita lihat bagaimana driver klien mengirimkan permintaan transfer massal sebagai akibat dari permintaan aplikasi atau driver lain. Atau, pengemudi dapat memulai transfer sendiri. Terlepas dari pendekatan, driver harus memiliki buffer transfer dan permintaan untuk memulai transfer massal.

Untuk driver KMDF, permintaan dijelaskan dalam objek permintaan kerangka kerja (lihat Referensi Objek Permintaan WDF). Driver klien memanggil metode objek permintaan dengan menentukan handel WDFREQUEST untuk mengirim permintaan ke tumpukan driver USB. Jika driver klien mengirim transfer massal sebagai respons terhadap permintaan dari aplikasi atau driver lain, kerangka kerja membuat objek permintaan dan mengirimkan permintaan ke driver klien dengan menggunakan objek antrean kerangka kerja. Dalam hal ini, driver klien dapat menggunakan permintaan tersebut untuk tujuan pengiriman transfer massal. Jika driver klien memulai permintaan, driver dapat memilih untuk mengalokasikan objek permintaannya sendiri.

Jika aplikasi atau driver lain mengirim atau meminta data, buffer transfer diteruskan ke driver oleh kerangka kerja. Atau, driver klien dapat mengalokasikan buffer transfer dan membuat objek permintaan jika driver memulai transfer sendiri.

Berikut adalah tugas utama untuk driver klien:

  1. Dapatkan buffer transfer.
  2. Dapatkan, format, dan kirim objek permintaan kerangka kerja ke tumpukan driver USB.
  3. Terapkan rutinitas penyelesaian untuk mendapatkan pemberitahuan ketika tumpukan driver USB menyelesaikan permintaan.

Topik ini menjelaskan tugas-tugas tersebut dengan menggunakan contoh di mana driver memulai transfer massal sebagai akibat dari permintaan aplikasi untuk mengirim atau menerima data.

Untuk membaca data dari perangkat, driver klien dapat menggunakan objek pembaca berkelanjutan yang disediakan kerangka kerja. Untuk informasi selengkapnya, lihat Cara menggunakan pembaca berkelanjutan untuk membaca data dari pipa USB.

Contoh permintaan transfer massal

Pertimbangkan contoh skenario, di mana aplikasi ingin membaca atau menulis data ke perangkat Anda. Aplikasi memanggil API Windows untuk mengirim permintaan tersebut. Dalam contoh ini, aplikasi membuka handel ke perangkat dengan menggunakan GUID antarmuka perangkat yang diterbitkan oleh driver Anda dalam mode kernel. Aplikasi kemudian memanggil ReadFile atau WriteFile untuk memulai permintaan baca atau tulis. Dalam panggilan itu, aplikasi juga menentukan buffer yang berisi data untuk dibaca atau ditulis dan panjang buffer tersebut.

Manajer I/O menerima permintaan, membuat Paket Permintaan I/O (IRP), dan meneruskannya ke driver klien.

Kerangka kerja mencegat permintaan, membuat objek permintaan kerangka kerja, dan menambahkannya ke objek antrean kerangka kerja. Kerangka kerja kemudian memberi tahu driver klien bahwa permintaan baru sedang menunggu untuk diproses. Pemberitahuan itu dilakukan dengan memanggil rutinitas panggilan balik antrean driver untuk EvtIoRead atau EvtIoWrite.

Ketika kerangka kerja mengirimkan permintaan ke driver klien, kerangka kerja menerima parameter ini:

  • WDFQUEUE menangani objek antrean kerangka kerja yang berisi permintaan.
  • WDFREQUEST menangani objek permintaan kerangka kerja yang berisi detail tentang permintaan ini.
  • Panjang transfer, yaitu, jumlah byte yang akan dibaca atau ditulis.

Dalam implementasi driver klien EvtIoRead atau EvtIoWrite, driver memeriksa parameter permintaan dan dapat secara opsional melakukan pemeriksaan validasi.

Jika Anda menggunakan aliran titik akhir massal SuperSpeed, Anda akan mengirim permintaan dalam URB karena KMDF tidak mendukung aliran secara intrinsik. Untuk informasi tentang mengirimkan permintaan transfer ke aliran titik akhir massal, lihat Cara membuka dan menutup aliran statis di titik akhir massal USB.

Jika Anda tidak menggunakan aliran, Anda dapat menggunakan metode yang ditentukan KMDF untuk mengirim permintaan seperti yang dijelaskan dalam prosedur berikut:

Prasyarat

Sebelum memulai, pastikan Anda memiliki informasi ini:

  • Driver klien harus membuat objek perangkat target USB kerangka kerja dan mendapatkan handel WDFUSBDEVICE dengan memanggil metode WdfUsbTargetDeviceCreateWithParameters .

    Jika Anda menggunakan templat USB yang disediakan dengan Microsoft Visual Studio Professional 2012, kode templat melakukan tugas tersebut. Kode templat mendapatkan handel ke objek perangkat target dan disimpan dalam konteks perangkat. Untuk informasi selengkapnya, lihat "Kode sumber perangkat" di Memahami struktur kode driver klien USB (KMDF).

  • WDFREQUEST menangani objek permintaan kerangka kerja yang berisi detail tentang permintaan ini.

  • Jumlah byte yang akan dibaca atau ditulis.

  • WDFUSBPIPE menangani ke objek pipa kerangka kerja yang terkait dengan titik akhir target. Anda harus mendapatkan handel pipa selama konfigurasi perangkat dengan menghitung pipa. Untuk informasi selengkapnya, lihat Cara menghitung pipa USB.

    Jika titik akhir massal mendukung aliran, Anda harus memiliki handel pipa ke aliran. Untuk informasi selengkapnya, lihat Cara membuka dan menutup aliran statis di titik akhir massal USB.

Langkah 1: Dapatkan buffer transfer

Buffer transfer atau MDL buffer transfer berisi data yang akan dikirim atau diterima. Topik ini mengasumsikan bahwa Anda mengirim atau menerima data dalam buffer transfer. Buffer transfer dijelaskan dalam objek memori WDF (lihat Referensi Objek Memori WDF). Untuk mendapatkan objek memori yang terkait dengan buffer transfer, panggil salah satu metode berikut:

Driver klien tidak perlu melepaskan memori ini. Memori dikaitkan dengan objek permintaan induk dan dirilis ketika induk dilepaskan.

Langkah 2: Memformat dan mengirim objek permintaan kerangka kerja ke tumpukan driver USB

Anda dapat mengirim permintaan transfer secara asinkron atau sinkron.

Ini adalah metode asinkron:

Metode dalam daftar ini memformat permintaan. Jika Anda mengirim permintaan secara asinkron, atur pointer ke rutinitas penyelesaian yang diimplementasikan driver dengan memanggil metode WdfRequestSetCompletionRoutine (dijelaskan pada langkah berikutnya). Untuk mengirim permintaan, panggil metode WdfRequestSend .

Jika Anda mengirim permintaan secara sinkron, panggil metode ini:

Untuk contoh kode, lihat bagian Contoh dari topik referensi untuk metode tersebut.

Langkah 3: Menerapkan rutinitas penyelesaian untuk permintaan

Jika permintaan dikirim secara asinkron, Anda harus menerapkan rutinitas penyelesaian untuk mendapatkan pemberitahuan ketika tumpukan driver USB menyelesaikan permintaan. Setelah selesai, kerangka kerja memanggil rutinitas penyelesaian driver. Kerangka kerja meneruskan parameter ini:

  • WDFREQUEST menangani objek permintaan.
  • WDFIOTARGET menangani objek target I/O untuk permintaan tersebut.
  • Penunjuk ke struktur WDF_REQUEST_COMPLETION_PARAMS yang berisi informasi penyelesaian. Informasi khusus USB terkandung dalam anggota CompletionParams-Parameters.Usb>.
  • WDFCONTEXT menangani konteks yang ditentukan driver dalam panggilannya ke WdfRequestSetCompletionRoutine.

Dalam rutinitas penyelesaian, lakukan tugas-tugas ini:

  • Periksa status permintaan dengan mendapatkan nilai CompletionParams-IoStatus.Status>.

  • Periksa status USBD yang diatur oleh tumpukan driver USB.

  • Jika terjadi kesalahan pipa, lakukan operasi pemulihan kesalahan. Untuk informasi selengkapnya, lihat Cara memulihkan dari kesalahan pipa USB.

  • Periksa jumlah byte yang ditransfer.

    Transfer massal selesai ketika jumlah byte yang diminta telah ditransfer ke atau dari perangkat. Jika Anda mengirim buffer permintaan dengan memanggil metode KMDF, periksa nilai yang diterima di CompletionParams-Parameters.Usb.Completion-Parameters.PipeWrite.Length>> atau CompletionParams-Parameters.Usb.Completion-Parameters.PipeRead.Length>>.

    Dalam transfer sederhana di mana tumpukan driver USB mengirim semua byte yang diminta dalam satu paket data, Anda dapat memeriksa membandingkan nilai Panjang dengan jumlah byte yang diminta. Jika tumpukan driver USB mentransfer permintaan dalam beberapa paket data, Anda harus melacak jumlah byte yang ditransfer dan jumlah byte yang tersisa.

  • Jika jumlah total byte ditransfer, selesaikan permintaan. Jika terjadi kondisi kesalahan, selesaikan permintaan dengan kode kesalahan yang dikembalikan. Selesaikan permintaan dengan memanggil metode WdfRequestComplete . Jika Anda ingin mengatur informasi, seperti jumlah byte yang ditransfer, hubungi WdfRequestCompleteWithInformation.

  • Pastikan bahwa ketika Anda menyelesaikan permintaan dengan informasi, jumlah byte harus sama dengan atau kurang dari jumlah byte yang diminta. Kerangka kerja memvalidasi nilai-nilai tersebut. Jika panjang yang diatur dalam permintaan yang diselesaikan lebih besar dari panjang permintaan asli, bugcheck dapat terjadi.

Contoh kode ini menunjukkan bagaimana driver klien dapat mengirimkan permintaan transfer massal. Driver menetapkan rutinitas penyelesaian. Rutinitas tersebut ditampilkan di blok kode berikutnya.

/*++

Routine Description:

This routine sends a bulk write request to the
USB driver stack. The request is sent asynchronously and
the driver gets notified through a completion routine.

Arguments:

Queue - Handle to a framework queue object.
Request - Handle to the framework request object.
Length - Number of bytes to transfer.


Return Value:

VOID

--*/


VOID Fx3EvtIoWrite(
    IN WDFQUEUE  Queue,
    IN WDFREQUEST  Request,
    IN size_t  Length
    )
{
    NTSTATUS  status;
    WDFUSBPIPE  pipe;
    WDFMEMORY  reqMemory;
    PDEVICE_CONTEXT  pDeviceContext;

    pDeviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue));

    pipe = pDeviceContext->BulkWritePipe;

    status = WdfRequestRetrieveInputMemory(
                                           Request,
                                           &reqMemory
                                           );
    if (!NT_SUCCESS(status))
    {
        goto Exit;
    }

    status = WdfUsbTargetPipeFormatRequestForWrite(
                                                   pipe,
                                                   Request,
                                                   reqMemory,
                                                   NULL
                                                   );
    if (!NT_SUCCESS(status))
       {
        goto Exit;
    }

    WdfRequestSetCompletionRoutine(
                                   Request,
                                   BulkWriteComplete,
                                   pipe
                                   );

    if (WdfRequestSend( Request,
                        WdfUsbTargetPipeGetIoTarget(pipe),
                        WDF_NO_SEND_OPTIONS) == FALSE)
       {
        status = WdfRequestGetStatus(Request);
        goto Exit;
    }

Exit:
    if (!NT_SUCCESS(status)) {
        WdfRequestCompleteWithInformation(
                                          Request,
                                          status,
                                          0
                                          );
    }
    return;
}

Contoh kode ini menunjukkan implementasi rutin penyelesaian untuk transfer massal. Driver klien menyelesaikan permintaan dalam rutinitas penyelesaian dan mengatur informasi permintaan ini: status dan jumlah byte yang ditransfer.

/*++

Routine Description:

This completion routine is invoked by the framework when
the USB drive stack completes the previously sent
bulk write request. The client driver completes the
the request if the total number of bytes were transferred
to the device.
In case of failure it queues a work item to start the
error recovery by resetting the target pipe.

Arguments:

Queue - Handle to a framework queue object.
Request - Handle to the framework request object.
Length - Number of bytes to transfer.
Pipe - Handle to the pipe that is the target for this request.

Return Value:

VOID

--*/

VOID BulkWriteComplete(
    _In_ WDFREQUEST                  Request,
    _In_ WDFIOTARGET                 Target,
    PWDF_REQUEST_COMPLETION_PARAMS   CompletionParams,
    _In_ WDFCONTEXT                  Context
    )
{

    PDEVICE_CONTEXT deviceContext;

    size_t          bytesTransferred=0;

    NTSTATUS        status;


    UNREFERENCED_PARAMETER (Target);
    UNREFERENCED_PARAMETER (Context);


    KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
        "In completion routine for Bulk transfer.\n"));

    // Get the device context. This is the context structure that
    // the client driver provided when it sent the request.

    deviceContext = (PDEVICE_CONTEXT)Context;

    // Get the status of the request
    status = CompletionParams->IoStatus.Status;
    if (!NT_SUCCESS (status))
    {
        // Get the USBD status code for more information about the error condition.
        status = CompletionParams->Parameters.Usb.Completion->UsbdStatus;

        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
            "Bulk transfer failed. 0x%x\n",
            status));

        // Queue a work item to start the reset-operation on the pipe
        // Not shown.

        goto Exit;
    }

    // Get the actual number of bytes transferred.
    bytesTransferred =
            CompletionParams->Parameters.Usb.Completion->Parameters.PipeWrite.Length;

    KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
            "Bulk transfer completed. Transferred %d bytes. \n",
            bytesTransferred));

Exit:

    // Complete the request and update the request with
    // information about the status code and number of bytes transferred.

    WdfRequestCompleteWithInformation(Request, status, bytesTransferred);

    return;
}