Berbagi teknologi

Pengembangan driver perangkat USB Windows yang memproses aliran statis transfer batch

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Di USB 2.0 dan perangkat yang lebih lama, titik akhir massal dapat mengirim atau menerima aliran data tunggal melalui titik akhir. Pada perangkat USB 3.0, titik akhir massal mampu mengirim dan menerima beberapa aliran data melalui titik akhir.

Tumpukan driver USB yang disediakan oleh Microsoft di Windows mendukung banyak aliran. Hal ini memungkinkan driver klien mengirim permintaan I/O independen ke setiap aliran yang terkait dengan titik akhir massal di perangkat USB 3.0, tanpa membuat serial permintaan ke aliran berbeda.

Bagi driver klien, aliran mewakili beberapa titik akhir logis dengan serangkaian karakteristik yang sama. Untuk mengirim permintaan ke aliran tertentu, driver klien memerlukan pegangan untuk aliran tersebut (mirip dengan pegangan pipa titik akhir). URB untuk permintaan I/O streaming mirip dengan URB untuk permintaan I/O untuk titik akhir massal. Satu-satunya perbedaan adalah pegangan pipanya. Untuk mengirim permintaan I/O ke aliran, driver menentukan pegangan pipa di aliran.

Selama konfigurasi perangkat, driver klien mengirimkan permintaan konfigurasi pilihan dan permintaan antarmuka pilihan. Permintaan ini mengambil sekumpulan pegangan pipa untuk titik akhir yang ditentukan dalam pengaturan aktivitas antarmuka. Untuk titik akhir yang sadar aliran, pegangan pipa titik akhir dapat digunakan untuk mengirim permintaan I/O ke aliran default (aliran pertama) hingga driver membuka aliran tersebut.

Jika driver klien ingin mengirim permintaan ke aliran selain aliran default, pengemudi harus membuka dan mendapatkan pegangan untuk semua aliran. Untuk melakukan hal ini, driver klien mengirimkan permintaan aliran terbuka dengan menentukan jumlah aliran yang akan dibuka. Setelah driver klien selesai menggunakan stream, driver dapat memilih untuk menutupnya dengan mengirimkan permintaan penutupan stream.

Kerangka Pengandar Mode Kernel (KMDF) tidak mendukung aliran statis. Driver klien harus menggunakan Windows Driver Model (WDM) untuk membuka dan menutup stream. Driver klien Kerangka Pengandar mode pengguna (UMDF) tidak dapat menggunakan fitur streaming statis.

Berikut ini mungkin berisi beberapa komentar berlabel Driver WDM. Petunjuk ini menjelaskan rutinitas untuk driver klien USB berbasis WDM yang ingin mengirim permintaan streaming.

prasyarat

Sebelum driver klien dapat membuka atau menutup aliran, driver harus memiliki:

1. Panggil metode WdfUsbTargetDeviceCreateWithParameters. Metode memerlukan versi protokol klien USBD_CLIENT_CONTRACT_VERSION_602. Dengan menentukan versi ini, driver klien harus mematuhi serangkaian aturan.

Panggilan untuk mengambil pegangan WDFUSBDEVICE dari objek perangkat target USB kerangka kerja. Pegangan ini diperlukan untuk panggilan selanjutnya ke aliran terbuka. Biasanya, driver klien mendaftarkan dirinya dalam rutinitas panggilan balik kejadian EVT_WDF_DEVICE_PREPARE_HARDWARE driver.

Driver WDM: Panggil rutin USBD_CreateHandle dan dapatkan pegangan USBD yang didaftarkan oleh driver di tumpukan driver USB.

2. Mengonfigurasi perangkat dan memperoleh pegangan pipa WDFUSBPIPE untuk titik akhir massal yang mendukung streaming. Untuk mendapatkan pegangan pipa, panggil metode WdfUsbInterfaceGetConfiguredPipe pada pengaturan alternatif antarmuka saat ini dalam konfigurasi yang dipilih.

Driver WDM: Mendapatkan pegangan pipa USBD dengan mengirimkan permintaan konfigurasi-pilih atau antarmuka-pilih.

Cara membuka aliran statis

1. Tentukan apakah tumpukan driver USB dan pengontrol host yang mendasarinya mendukung fitur streaming statis dengan memanggil metode WdfUsbTargetDeviceQueryUsbCapability. Biasanya, driver klien memanggil rutinitas dalam rutinitas panggilan balik acara EVT_WDF_DEVICE_PREPARE_HARDWARE driver.

Driver WDM: Memanggil rutin USBD_QueryUsbCapability. Biasanya, driver menanyakan fungsi yang akan digunakan dalam rutinitas perangkat startup driver, (IRP_MN_START_DEVICE).

Berikan informasi berikut:

  • Pegangan ke objek perangkat USB yang diambil dalam panggilan sebelumnya ke WdfUsbTargetDeviceCreateWithParameters, digunakan untuk mendaftarkan driver klien.

Driver WDM: Teruskan pegangan USBD yang diambil pada panggilan sebelumnya ke USBD_CreateHandle.

Jika driver klien ingin menggunakan fitur tertentu, driver harus terlebih dahulu menanyakan tumpukan driver USB yang mendasarinya untuk menentukan apakah tumpukan driver dan pengontrol host mendukung fitur tersebut. Jika fitur tersebut didukung, barulah pengemudi dapat mengirimkan permintaan untuk menggunakan fitur tersebut. Permintaan tertentu memerlukan URB, seperti fungsi streaming yang dibahas pada Langkah 5. Untuk permintaan ini, pastikan untuk menggunakan pegangan yang sama untuk menanyakan fungsi dan mengalokasikan URB. Hal ini karena tumpukan driver menggunakan pegangan untuk melacak fitur-fitur pendukung yang dapat digunakan driver.

Misalnya, jika USBD_HANDLE diperoleh dengan memanggil USBD_CreateHandle, tumpukan driver akan ditanyakan dengan memanggil USBD_QueryUsbCapability dan URB dialokasikan dengan memanggil USBD_UrbAllocation. Lewatkan USBD_HANDLE yang sama di kedua panggilan.

Jika Anda memanggil metode KMDF, WdfUsbTargetDeviceQueryUsbCapability, dan WdfUsbTargetDeviceCreateUrb, tentukan pegangan WDFUSBDEVICE yang sama untuk objek target kerangka kerja dalam pemanggilan metode ini.

  • GUID ditetapkan ke GUID_USB_CAPABILITY_STATIC_STRREAMS;
  • Penunjuk ke buffer keluaran (menunjuk ke USHORT). Setelah selesai, buffer akan diisi dengan (jumlah maksimum aliran untuk) setiap titik akhir yang didukung oleh pengontrol host;
  • Panjang buffer keluaran, dalam byte. Untuk sungai, panjangnya adalah sizeof (USHORT);

2. Evaluasi nilai NTSTATUS yang dikembalikan. Jika rutinitas berhasil diselesaikan, STATUS_SUCCESS dikembalikan, dan fungsi streaming statis didukung. Jika tidak, metode ini akan mengembalikan kode kesalahan yang sesuai.

3. Tentukan jumlah aliran yang akan dibuka. Jumlah maksimum stream yang dapat dibuka dibatasi oleh:

  • Jumlah maksimum aliran yang didukung oleh pengontrol host. WdfUsbTargetDeviceQueryUsbCapability (menerima WdfUsbTargetDeviceQueryUsbCapabilityUSBD_QueryUsbCapability) di buffer output yang disediakan pemanggil. Tumpukan driver USB yang disediakan oleh Microsoft mendukung hingga 255 aliran. WdfUsbTargetDeviceQueryUsbCapability memperhitungkan batas ini saat menghitung jumlah aliran. Metode ini tidak pernah mengembalikan nilai lebih besar dari 255.
  • Jumlah maksimum aliran yang didukung oleh titik akhir di perangkat. Untuk mendapatkan nomor ini, periksa deskriptor pendamping titik akhir (dalam Usbspec.h) untuk USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR. Untuk mendapatkan deskriptor pendamping titik akhir, deskriptor konfigurasi harus diurai. Untuk mendapatkan deskriptor konfigurasi, driver klien harus memanggil metode WdfUsbTargetDeviceRetrieveConfigDescriptor. Rutinitas pembantu, USBD_ParseConfigurationDescriptorEx dan USBD_ParseDescriptor, harus digunakan.

Untuk menentukan jumlah aliran maksimum, pilih nilai yang lebih kecil dari dua nilai yang didukung oleh pengontrol host dan titik akhir.

4. Alokasikan array struktur USBD_STREAM_INFORMATION n-elemen, di mana n adalah jumlah aliran yang akan dibuka. Driver klien bertanggung jawab untuk membebaskan array ini setelah driver selesai menggunakan aliran.

5. Alokasikan URB untuk permintaan aliran terbuka dengan memanggil metode WdfUsbTargetDeviceCreateUrb. Jika panggilan berhasil diselesaikan, metode ini mengambil objek memori WDF dan alamat struktur URB yang dialokasikan oleh tumpukan driver USB.

Driver WDM: Memanggil rutin USBD_UrbAllocation.

6. Atur format URB dari permintaan aliran terbuka. URB menggunakan struktur _URB_OPEN_STATIC_STRREAMS untuk menentukan permintaan. Untuk memformat URB, Anda memerlukan:

  • Pegangan pipa USBD menunjuk ke titik akhir. Jika ada objek pipa WDF, Anda bisa mendapatkan pegangan pipa USBD dengan memanggil metode WdfUsbTargetPipeWdmGetPipeHandle.
  • Dibuat pada langkah 4 (array aliran)
  • Penunjuk ke struktur URB yang dibuat pada (langkah 5).

Untuk memformat URB, panggil UsbBuildOpenStaticStreamsRequest dan teruskan informasi yang diperlukan sebagai nilai parameter. Pastikan jumlah aliran yang ditentukan ke UsbBuildOpenStaticStreamsRequest tidak melebihi jumlah maksimum aliran yang didukung.

7. Kirim URB sebagai objek permintaan WDF dengan memanggil metode WdfRequestSend. Untuk mengirim permintaan secara sinkron, panggil metode WdfUsbTargetDeviceSendUrbSynchronously.

Driver WDM: Mengaitkan URB dengan IRP dan mengirimkan IRP ke tumpukan driver USB.

8. Setelah permintaan selesai, periksa status permintaan.Jika permintaan tumpukan driver USB gagal, status URB berisi kode kesalahan yang relevan.

Jika status permintaan (objek permintaan IRP atau WDF) menunjukkan USBD_STATUS_SUCCESS, permintaan berhasil diselesaikan. Array struktur USBD_STREAM_INFORMATION diterima saat pemeriksaan selesai. Array diisi dengan informasi tentang aliran yang diminta. Tumpukan driver USB mengisi setiap struktur dalam array dengan informasi aliran, seperti pegangan yang diterima USBD_PIPE_HANDLE, pengidentifikasi aliran, dan ukuran transfer numerik maksimum. Aliran sekarang dapat mentransfer data.

Untuk permintaan aliran terbuka, URB dan array perlu dialokasikan. Setelah permintaan aliran terbuka selesai, driver klien harus melepaskan URB dengan memanggil metode WdfObjectDelete pada objek memori WDF terkait. Jika driver mengirimkan permintaan secara sinkron dengan memanggil WdfUsbTargetDeviceSendUrbSynchronously, objek memori WDF harus dilepaskan setelah metode kembali. Jika driver klien mengirimkan permintaan secara asinkron dengan memanggil WdfRequestSend, driver harus melepaskan objek memori WDF dalam rutinitas penyelesaian yang diimplementasikan driver yang terkait dengan permintaan tersebut.

Array aliran dapat dilepaskan setelah driver klien selesai menggunakan aliran, atau dapat disimpan untuk permintaan I/O. Dalam contoh kode yang disertakan di bawah, driver menyimpan array aliran dalam konteks perangkat. Driver melepaskan konteks perangkat sebelum melepaskan objek perangkat.

Cara mentransfer data ke aliran tertentu
Untuk mengirim permintaan transfer data ke aliran tertentu, diperlukan objek permintaan WDF. Biasanya, driver klien tidak perlu mengalokasikan objek permintaan WDF. Ketika manajer I/O menerima permintaan dari suatu aplikasi, manajer I/O membuat IRP untuk permintaan tersebut. IRP dicegat oleh kerangka kerja. Kerangka kerja kemudian mengalokasikan objek permintaan WDF untuk mewakili IRP. Setelah itu, kerangka kerja meneruskan objek permintaan WDF ke driver klien. Driver klien kemudian dapat mengaitkan objek permintaan dengan URB transfer data dan mengirimkannya ke tumpukan driver USB.

Jika driver klien tidak menerima objek permintaan WDF dari kerangka kerja dan ingin mengirim permintaan secara asinkron, driver harus mengalokasikan objek permintaan WDF dengan memanggil metode WdfRequestCreate. Format objek baru dengan memanggil WdfUsbTargetPipeFormatRequestForUrb dan kirim permintaan dengan memanggil WdfRequestSend.

Dalam kasus sinkron, meneruskan objek permintaan WDF adalah opsional.

Untuk mentransfer data ke aliran, URB harus digunakan. URB harus diformat dengan memanggil WdfUsbTargetPipeFormatRequestForUrb.

Streaming tidak mendukung metode WDF berikut:

  • WdfUsbTargetPipeFormatPermintaanUntukDibaca
  • WdfUsbTargetPipeFormatPermintaanUntukMenulis
  • WdfUsbTargetPipeBacaSecaraSinkron
  • WdfUsbTargetPipeTulisSecaraSinkron

Prosedur berikut mengasumsikan bahwa driver klien menerima objek permintaan dari kerangka kerja.

  1. Alokasikan URB dengan memanggil WdfUsbTargetDeviceCreateUrb. Metode ini mengalokasikan objek memori WDF yang berisi URB yang baru dialokasikan. Pengemudi klien dapat memilih untuk mengalokasikan URB untuk setiap permintaan I/O, atau mengalokasikan URB dan menggunakannya untuk permintaan dengan jenis yang sama.
  2. Format URB untuk transfer massal dengan memanggil UsbBuildInterruptOrBulkTransferRequest. Di parameter PipeHandle, tentukan pegangan aliran. Pegangan aliran diperoleh dalam permintaan sebelumnya, seperti yang dijelaskan di bagian Cara Membuka Aliran Statis.
  3. Format objek permintaan WDF dengan memanggil metode WdfUsbTargetPipeFormatRequestForUrb. Dalam panggilan tersebut, tentukan objek memori WDF yang berisi URB transfer data. Objek memori dialokasikan pada langkah 1.
  4. Kirim URB sebagai permintaan WDF dengan memanggil WdfRequestSend atau WdfUsbTargetPipeSendUrbSynchronously. Jika Anda memanggil WdfRequestSend, Anda harus menentukan rutinitas penyelesaian dengan memanggil WdfRequestSetCompletionRoutine sehingga driver klien dapat diberitahu ketika operasi asinkron selesai. URB transfer data harus dilepaskan dalam rutinitas penyelesaian.

Driver WDM: Mengalokasikan URB dengan memanggil USBD_UrbAllocation dan memformatnya untuk transfer massal (lihat _URB_BULK_OR_INTERRUPT_TRANSFER). Untuk memformat URB, Anda dapat memanggil UsbBuildInterruptOrBulkTransferRequest atau memformat struktur URB secara manual. Tentukan pegangan aliran di anggota UrbBulkOrInterruptTransfer.PipeHandle dari URB.

Cara menutup aliran statis

Driver klien dapat menutup aliran setelah driver selesai menggunakannya. Namun, menutup permintaan streaming bersifat opsional. Tumpukan driver USB menutup semua aliran ketika titik akhir yang terkait dengan aliran tersebut tidak dikonfigurasi. Titik akhir tidak dikonfigurasi ketika memilih konfigurasi atau antarmuka alternatif, menghapus perangkat, dll. Jika driver klien ingin membuka sejumlah aliran yang berbeda, ia harus menutup aliran tersebut. Kirim permintaan penutupan streaming:

1. Alokasikan struktur URB dengan memanggil WdfUsbTargetDeviceCreateUrb.

2. Atur format URB untuk menutup permintaan streaming. Anggota UrbPipeRequest dari struktur URB adalah struktur _URB_PIPE_REQUEST. Isikan anggotanya sebagai berikut:

  • Diperlukan anggota Hdr dari URB_FUNCTION_CLOSE_STATIC_STRREAMS_URB_PIPE_REQUEST
  • Anggota PipeHandle harus berupa pegangan yang berisi titik akhir yang digunakan untuk membuka aliran.

3. Kirim URB sebagai permintaan WDF dengan memanggil WdfRequestSend atau WdfUsbTargetDeviceSendUrbSynchronously.

Pegangan tutup meminta penutupan semua aliran yang sebelumnya dibuka oleh driver klien. Pengandar klien tidak dapat menggunakan permintaan untuk menutup aliran tertentu di titik akhir.

Praktik terbaik untuk mengirimkan permintaan streaming statis

Tumpukan driver USB melakukan verifikasi pada URB yang diterima. Untuk menghindari kesalahan validasi, lakukan hal berikut:

  • Jangan mengirim permintaan aliran terbuka atau aliran tertutup ke titik akhir yang tidak mendukung aliran. Panggil WdfUsbTargetDeviceQueryUsbCapability (, USBD_QueryUsbCapability) driver WDM untuk menentukan dukungan streaming statis dan hanya mengirim permintaan streaming jika titik akhir mendukungnya.
  • Jangan meminta aliran lebih banyak (terbuka) dari jumlah maksimum aliran yang didukung, atau mengirimkan permintaan tanpa menentukan jumlah aliran. Jumlah aliran ditentukan berdasarkan jumlah aliran yang didukung oleh tumpukan driver USB dan titik akhir perangkat.
  • Jangan mengirim permintaan aliran terbuka ke titik akhir yang sudah memiliki aliran terbuka.
  • Jangan mengirim permintaan aliran dekat ke titik akhir yang tidak memiliki aliran terbuka.
  • Setelah Anda membuka aliran statis untuk titik akhir, jangan mengirim permintaan I/O menggunakan pegangan pipa titik akhir yang diperoleh melalui konfigurasi pilihan atau permintaan antarmuka pilihan. Hal ini berlaku meskipun aliran statis ditutup.
Setel ulang dan batalkan operasi saluran pipa

Terkadang, transfer ke atau dari titik akhir mungkin gagal. Kegagalan tersebut mungkin disebabkan oleh kondisi kesalahan pada titik akhir atau pengontrol host, seperti kondisi terhenti atau berhenti. Untuk menghapus kondisi kesalahan, driver klien terlebih dahulu membatalkan transfer yang tertunda dan kemudian mengatur ulang pipa yang terkait dengan titik akhir. Untuk membatalkan transfer yang tertunda, driver klien dapat mengirimkan permintaan pembatalan pipa. Untuk mereset pipa, driver klien harus mengirimkan permintaan reset pipa.

Untuk streaming, permintaan batalkan pipa dan setel ulang pipa tidak didukung untuk aliran individual yang terkait dengan titik akhir massal. Jika transfer pada pipa aliran tertentu gagal, pengontrol host akan menghentikan transfer pada semua pipa lain untuk aliran lainnya. Untuk memulihkan kondisi kesalahan, driver klien harus membatalkan transfer ke setiap aliran secara manual pegangan pipa untuk mengirim permintaan reset pipa ke titik akhir massal. Untuk permintaan ini, driver klien harus menentukan pegangan pipa titik akhir dalam struktur _URB_PIPE_REQUEST dan mengatur fungsi URB (Hdr.Function) ke URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL.

Contoh lengkap

Contoh kode berikut menunjukkan cara membuka aliran.

  1. NTSTATUS
  2. OpenStreams (
  3. _In_ WDFDEVICE Device,
  4. _In_ WDFUSBPIPE Pipe)
  5. {
  6. NTSTATUS status;
  7. PDEVICE_CONTEXT deviceContext;
  8. PPIPE_CONTEXT pipeContext;
  9. USHORT cStreams = 0;
  10. USBD_PIPE_HANDLE usbdPipeHandle;
  11. WDFMEMORY urbMemory = NULL;
  12. PURB urb = NULL;
  13. PAGED_CODE();
  14. deviceContext =GetDeviceContext(Device);
  15. pipeContext = GetPipeContext (Pipe);
  16. if (deviceContext->MaxStreamsController == 0)
  17. {
  18. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  19. "%!FUNC! Static streams are not supported.");
  20. status = STATUS_NOT_SUPPORTED;
  21. goto Exit;
  22. }
  23. // If static streams are not supported, number of streams supported is zero.
  24. if (pipeContext->MaxStreamsSupported == 0)
  25. {
  26. status = STATUS_DEVICE_CONFIGURATION_ERROR;
  27. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  28. "%!FUNC! Static streams are not supported by the endpoint.");
  29. goto Exit;
  30. }
  31. // Determine the number of streams to open.
  32. // Compare the number of streams supported by the endpoint with the
  33. // number of streams supported by the host controller, and choose the
  34. // lesser of the two values. The deviceContext->MaxStreams value was
  35. // obtained in a previous call to WdfUsbTargetDeviceQueryUsbCapability
  36. // that determined whether or not static streams is supported and
  37. // retrieved the maximum number of streams supported by the
  38. // host controller. The device context stores the values for IN and OUT
  39. // endpoints.
  40. // Allocate an array of USBD_STREAM_INFORMATION structures to store handles to streams.
  41. // The number of elements in the array is the number of streams to open.
  42. // The code snippet stores the array in its device context.
  43. cStreams = min(deviceContext->MaxStreamsController, pipeContext->MaxStreamsSupported);
  44. // Allocate an array of streams associated with the IN bulk endpoint
  45. // This array is released in CloseStreams.
  46. pipeContext->StreamInfo = (PUSBD_STREAM_INFORMATION) ExAllocatePoolWithTag (
  47. NonPagedPool,
  48. sizeof (USBD_STREAM_INFORMATION) * cStreams,
  49. USBCLIENT_TAG);
  50. if (pipeContext->StreamInfo == NULL)
  51. {
  52. status = STATUS_INSUFFICIENT_RESOURCES;
  53. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  54. "%!FUNC! Could not allocate stream information array.");
  55. goto Exit;
  56. }
  57. RtlZeroMemory (pipeContext->StreamInfo,
  58. sizeof (USBD_STREAM_INFORMATION) * cStreams);
  59. // Get USBD pipe handle from the WDF target pipe object. The client driver received the
  60. // endpoint pipe handles during device configuration.
  61. usbdPipeHandle = WdfUsbTargetPipeWdmGetPipeHandle (Pipe);
  62. // Allocate an URB for the open streams request.
  63. // WdfUsbTargetDeviceCreateUrb returns the address of the
  64. // newly allocated URB and the WDFMemory object that
  65. // contains the URB.
  66. status = WdfUsbTargetDeviceCreateUrb (
  67. deviceContext->UsbDevice,
  68. NULL,
  69. &urbMemory,
  70. &urb);
  71. if (status != STATUS_SUCCESS)
  72. {
  73. TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
  74. "%!FUNC! Could not allocate URB for an open-streams request.");
  75. goto Exit;
  76. }
  77. // Format the URB for the open-streams request.
  78. // The UsbBuildOpenStaticStreamsRequest inline function formats the URB by specifying the
  79. // pipe handle to the entire bulk endpoint, number of streams to open, and the array of stream structures.
  80. UsbBuildOpenStaticStreamsRequest (
  81. urb,
  82. usbdPipeHandle,
  83. (USHORT)cStreams,
  84. pipeContext->StreamInfo);
  85. // Send the request synchronously.
  86. // Upon completion, the USB driver stack populates the array of with handles to streams.
  87. status = WdfUsbTargetPipeSendUrbSynchronously (
  88. Pipe,
  89. NULL,
  90. NULL,
  91. urb);
  92. if (status != STATUS_SUCCESS)
  93. {
  94. goto Exit;
  95. }
  96. Exit:
  97. if (urbMemory)
  98. {
  99. WdfObjectDelete (urbMemory);
  100. }
  101. return status;
  102. }