Menulis driver sumber HID dengan menggunakan Virtual HID Framework (VHF)

Topik ini menjelaskan cara:

  • Tulis driver sumber Kernel-Mode Driver Framework (KMDF)HID yang mengirimkan laporan baca HID ke Windows.
  • Muat driver VHF sebagai filter yang lebih rendah ke driver sumber HID di tumpukan perangkat HID virtual.

Pelajari tentang menulis driver sumber HID yang melaporkan data HID ke sistem operasi.

Perangkat input HID, seperti – keyboard, mouse, pena, sentuhan, atau tombol, mengirim berbagai laporan ke sistem operasi sehingga dapat memahami tujuan perangkat dan mengambil tindakan yang diperlukan. Laporan tersebut berupa koleksi HID dan Penggunaan HID. Perangkat mengirimkan laporan tersebut melalui berbagai transportasi, beberapa di antaranya didukung oleh Windows, seperti HID melalui I2C dan HID melalui USB. Dalam beberapa kasus, transportasi mungkin tidak didukung oleh Windows, atau laporan mungkin tidak langsung dipetakan ke perangkat keras nyata. Ini bisa menjadi aliran data dalam format HID yang dikirim oleh komponen perangkat lunak lain untuk perangkat keras virtual seperti, untuk tombol atau sensor non-GPIO. Misalnya, pertimbangkan data akselerometer dari ponsel yang berkinerja sebagai pengontrol game, dikirim secara nirkabel ke PC. Dalam contoh lain, komputer dapat menerima input jarak jauh dari perangkat Miracast dengan menggunakan protokol UIBC.

Di versi Windows sebelumnya, untuk mendukung transportasi baru (perangkat keras atau perangkat lunak nyata), Anda harus menulis minidriver transportasi HID dan mengikatnya ke driver kelas dalam kotak yang disediakan Microsoft, Hidclass.sys. Pasangan driver kelas/mini menyediakan koleksi HID, seperti Koleksi Tingkat Atas ke driver tingkat atas dan aplikasi mode pengguna. Dalam model itu, tantangannya adalah menulis minidriver, yang bisa menjadi tugas yang kompleks.

Mulai Windows 10, Virtual HID Framework (VHF) baru menghilangkan kebutuhan untuk menulis minidriver transportasi. Sebagai gantinya, Anda dapat menulis driver sumber HID dengan menggunakan antarmuka pemrograman KMDF atau WDM. Kerangka kerja ini terdiri dari pustaka statis yang disediakan Microsoft yang mengekspos elemen pemrograman yang digunakan oleh driver Anda. Ini juga termasuk driver dalam kotak yang disediakan Microsoft yang menghitung satu atau beberapa perangkat anak dan melanjutkan untuk membangun dan mengelola pohon HID virtual.

Catatan

Dalam rilis ini, VHF mendukung driver sumber HID hanya dalam mode kernel.

Topik ini menjelaskan arsitektur kerangka kerja, pohon perangkat HID virtual, dan skenario konfigurasi.

Pohon perangkat HID virtual

Dalam gambar ini, pohon perangkat menunjukkan driver dan objek perangkat terkait.

Diagram pohon perangkat HID virtual.

Driver sumber HID (driver Anda)

Driver sumber HID terhubung ke Vhfkm.lib dan menyertakan Vhf.h dalam proyek build-nya. Driver dapat ditulis dengan menggunakan Windows Driver Model (WDM) atau Kernel-Mode Driver Framework (KMDF) yang merupakan bagian dari Windows Driver Frameworks (WDF). Pengandar dapat dimuat sebagai pengandar filter atau driver fungsi di tumpukan perangkat.

Pustaka statis VHF (vhfkm.lib)

Pustaka statis disertakan dalam Windows Driver Kit (WDK) untuk Windows 10. Pustaka mengekspos antarmuka pemrograman seperti rutinitas dan fungsi panggilan balik yang digunakan oleh driver sumber HID Anda. Saat driver Anda memanggil fungsi, pustaka statis meneruskan permintaan ke driver VHF yang menangani permintaan.

Driver VHF (Vhf.sys)

Driver dalam kotak yang disediakan Microsoft. Pengandar ini harus dimuat sebagai pengandar filter yang lebih rendah di bawah driver Anda di tumpukan perangkat sumber HID. Driver VHF secara dinamis menghitung perangkat anak dan membuat objek perangkat fisik (PDO) untuk satu atau beberapa perangkat HID yang ditentukan oleh driver sumber HID Anda. Ini juga mengimplementasikan fungsionalitas driver mini HID Transport dari perangkat anak yang dijumlahkan.

Pasangan driver kelas HID (Hidclass.sys, Mshidkmdf.sys)

Pasangan Hidclass/Mshidkmdf menghitung Koleksi Tingkat Atas (TLC) mirip dengan cara menghitung koleksi tersebut untuk perangkat HID nyata. Klien HID dapat terus meminta dan menggunakan TLC seperti perangkat HID nyata. Pasangan driver ini diinstal sebagai driver fungsi di tumpukan perangkat.

Catatan

Dalam beberapa skenario, klien HID mungkin perlu mengidentifikasi sumber data HID. Misalnya, sistem memiliki sensor bawaan dan menerima data dari sensor jarak jauh dengan jenis yang sama. Sistem mungkin ingin memilih satu sensor agar lebih dapat diandalkan. Untuk membedakan antara dua sensor yang terhubung ke sistem, klien HID meminta ID kontainer TLC. Dalam hal ini, driver sumber HID dapat memberikan ID kontainer, yang dilaporkan sebagai ID kontainer perangkat HID virtual oleh VHF.

Klien HID (aplikasi)

Mengkueri dan menggunakan TLC yang dilaporkan oleh tumpukan perangkat HID.

Persyaratan header dan pustaka

Prosedur ini menjelaskan cara menulis driver sumber HID sederhana yang melaporkan tombol headset ke sistem operasi. Dalam hal ini, driver yang mengimplementasikan kode ini dapat menjadi driver audio KMDF yang ada yang telah dimodifikasi untuk bertindak sebagai tombol headset pelaporan sumber HID dengan menggunakan VHF.

  1. Sertakan Vhf.h, termasuk dalam WDK untuk Windows 10.

  2. Tautan ke vhfkm.lib, termasuk dalam WDK.

  3. Buat Deskriptor Laporan HID yang ingin dilaporkan perangkat Anda ke sistem operasi. Dalam contoh ini, Deskriptor Laporan HID menjelaskan tombol headset. Laporan menentukan Laporan Input HID, ukuran 8 bit (1 byte). Tiga bit pertama adalah untuk tombol tengah headset, volume-naik, dan volume-turun. Bit yang tersisa tidak digunakan.

    UCHAR HeadSetReportDescriptor[] = {
        0x05, 0x01,         // USAGE_PAGE (Generic Desktop Controls)
        0x09, 0x0D,         // USAGE (Portable Device Buttons)
        0xA1, 0x01,         // COLLECTION (Application)
        0x85, 0x01,         //   REPORT_ID (1)
        0x05, 0x09,         //   USAGE_PAGE (Button Page)
        0x09, 0x01,         //   USAGE (Button 1 - HeadSet : middle button)
        0x09, 0x02,         //   USAGE (Button 2 - HeadSet : volume up button)
        0x09, 0x03,         //   USAGE (Button 3 - HeadSet : volume down button)
        0x15, 0x00,         //   LOGICAL_MINIMUM (0)
        0x25, 0x01,         //   LOGICAL_MAXIMUM (1)
        0x75, 0x01,         //   REPORT_SIZE (1)
        0x95, 0x03,         //   REPORT_COUNT (3)
        0x81, 0x02,         //   INPUT (Data,Var,Abs)
        0x95, 0x05,         //   REPORT_COUNT (5)
        0x81, 0x03,         //   INPUT (Cnst,Var,Abs)
        0xC0,               // END_COLLECTION
    };
    

Membuat perangkat HID virtual

Inisialisasi struktur VHF_CONFIG dengan memanggil makro VHF_CONFIG_INIT lalu panggil metode VhfCreate . Driver harus memanggil VhfCreate pada PASSIVE_LEVEL setelah panggilan WdfDeviceCreate , biasanya, dalam fungsi panggilan balik EvtDriverDeviceAdd driver.

Dalam panggilan VhfCreate , driver dapat menentukan opsi konfigurasi tertentu, seperti operasi yang harus diproses secara asinkron atau mengatur informasi perangkat (ID vendor/produk).

Misalnya, aplikasi meminta TLC. Ketika pasangan driver kelas HID menerima permintaan tersebut, pasangan menentukan jenis permintaan dan membuat permintaan HID Minidriver IOCTL yang sesuai dan meneruskannya ke VHF. Setelah mendapatkan permintaan IOCTL, VHF dapat menangani permintaan, mengandalkan driver sumber HID untuk memprosesnya, atau menyelesaikan permintaan dengan STATUS_NOT_SUPPORTED.

VHF menangani IOCTL ini:

Jika permintaannya adalah GetFeature, SetFeature, WriteReport, atau GetInputReport, dan driver sumber HID mendaftarkan fungsi panggilan balik yang sesuai, VHF memanggil fungsi panggilan balik. Dalam fungsi itu, driver sumber HID bisa mendapatkan atau mengatur data HID untuk perangkat virtual HID. Jika driver tidak mendaftarkan panggilan balik, VHF menyelesaikan permintaan dengan status STATUS_NOT_SUPPORTED.

VHF memanggil fungsi panggilan balik peristiwa yang diimplementasikan driver sumber HID untuk IOCTL ini:

Untuk HID Minidriver IOCTL lainnya, VHF menyelesaikan permintaan dengan STATUS_NOT_SUPPORTED.

Perangkat HID virtual dihapus dengan memanggil VhfDelete. Panggilan balik EvtVhfCleanup diperlukan jika driver mengalokasikan sumber daya untuk perangkat HID virtual. Driver harus mengimplementasikan fungsi EvtVhfCleanup dan menentukan pointer ke fungsi tersebut di anggota VHF_CONFIG EvtVhfCleanup. EvtVhfCleanup dipanggil sebelum panggilan VhfDelete selesai. Untuk informasi selengkapnya, lihat Menghapus perangkat HID virtual.

Catatan

Setelah operasi asinkron selesai, driver harus memanggil VhfAsyncOperationComplete untuk mengatur hasil operasi. Anda dapat memanggil metode dari panggilan balik peristiwa atau di lain waktu setelah kembali dari panggilan balik.

NTSTATUS
VhfSourceCreateDevice(
_Inout_ PWDFDEVICE_INIT DeviceInit
)

{
    WDF_OBJECT_ATTRIBUTES   deviceAttributes;
    PDEVICE_CONTEXT deviceContext;
    VHF_CONFIG vhfConfig;
    WDFDEVICE device;
    NTSTATUS status;

    PAGED_CODE();

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
    deviceAttributes.EvtCleanupCallback = VhfSourceDeviceCleanup;

    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);

    if (NT_SUCCESS(status))
    {
        deviceContext = DeviceGetContext(device);

        VHF_CONFIG_INIT(&vhfConfig,
            WdfDeviceWdmGetDeviceObject(device),
            sizeof(VhfHeadSetReportDescriptor),
            VhfHeadSetReportDescriptor);

        status = VhfCreate(&vhfConfig, &deviceContext->VhfHandle);

        if (!NT_SUCCESS(status)) {
            TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "VhfCreate failed %!STATUS!", status);
            goto Error;
        }

        status = VhfStart(deviceContext->VhfHandle);
        if (!NT_SUCCESS(status)) {
            TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "VhfStart failed %!STATUS!", status);
            goto Error;
        }

    }

Error:
    return status;
}

Mengirimkan laporan input HID

Kirim laporan input HID dengan memanggil VhfReadReportSubmit.

Biasanya, perangkat HID mengirimkan informasi tentang perubahan status dengan mengirim laporan input melalui gangguan. Misalnya, perangkat headset mungkin mengirim laporan saat status tombol berubah. Dalam peristiwa seperti itu, rutinitas layanan interupsi driver (ISR) dipanggil. Dalam rutinitas itu, driver mungkin menjadwalkan panggilan prosedur yang ditangguhkan (DPC) yang memproses laporan input dan mengirimkannya ke VHF, yang mengirimkan informasi ke sistem operasi. Secara default, VHF menyangga laporan dan driver sumber HID dapat mulai mengirimkan Laporan Input HID saat masuk. Ini dan menghilangkan kebutuhan driver sumber HID untuk menerapkan sinkronisasi yang kompleks.

Driver sumber HID dapat mengirimkan laporan input dengan menerapkan kebijakan buffering untuk laporan yang tertunda. Untuk menghindari buffering duplikat, driver sumber HID dapat mengimplementasikan fungsi panggilan balik EvtVhfReadyForNextReadReport dan melacak apakah VHF memanggil panggilan balik ini. Jika sebelumnya dipanggil, driver sumber HID dapat memanggil VhfReadReportSubmit untuk mengirimkan laporan. Harus menunggu EvtVhfReadyForNextReadReport dipanggil sebelum dapat memanggil VhfReadReportSubmit lagi.

VOID
MY_SubmitReadReport(
    PMY_CONTEXT  Context,
    BUTTON_TYPE  ButtonType,
    BUTTON_STATE ButtonState
    )
{
    PDEVICE_CONTEXT deviceContext = (PDEVICE_CONTEXT)(Context);

    if (ButtonState == ButtonStateUp) {
        deviceContext->VhfHidReport.ReportBuffer[0] &= ~(0x01 << ButtonType);
    } else {
        deviceContext->VhfHidReport.ReportBuffer[0] |=  (0x01 << ButtonType);
    }

    status = VhfReadReportSubmit(deviceContext->VhfHandle, &deviceContext->VhfHidReport);

    if (!NT_SUCCESS(status)) {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,"VhfReadReportSubmit failed %!STATUS!", status);
    }
}

Menghapus perangkat HID virtual

Hapus perangkat HID virtual dengan memanggil VhfDelete.

VhfDelete dapat disebut sinkron atau asinkron dengan menentukan parameter Tunggu. Untuk panggilan sinkron, metode harus dipanggil di PASSIVE_LEVEL, seperti dari EvtCleanupCallback objek perangkat. VhfDelete kembali setelah menghapus perangkat HID virtual. Jika driver memanggil VhfDelete secara asinkron, driver akan segera kembali dan VHF memanggil EvtVhfCleanup setelah operasi penghapusan selesai. Metode ini dapat dipanggil pada DISPATCH_LEVEL maksimum. Dalam hal ini, driver harus telah mendaftar dan mengimplementasikan fungsi panggilan balik EvtVhfCleanup ketika sebelumnya disebut VhfCreate. Berikut adalah urutan peristiwa ketika driver sumber HID ingin menghapus perangkat HID virtual:

  1. Driver sumber HID berhenti memulai panggilan ke VHF.
  2. Sumber HID memanggil VhfDelete dengan Tunggu diatur ke FALSE.
  3. VHF berhenti memanggil fungsi panggilan balik yang diterapkan oleh driver sumber HID.
  4. VHF mulai melaporkan perangkat sebagai hilang ke PnP Manager. Pada titik ini, panggilan VhfDelete mungkin kembali.
  5. Ketika perangkat dilaporkan sebagai perangkat yang hilang, VHF memanggil EvtVhfCleanup jika driver sumber HID mendaftarkan implementasinya.
  6. Setelah EvtVhfCleanup kembali, VHF melakukan pembersihan.
VOID
VhfSourceDeviceCleanup(
_In_ WDFOBJECT DeviceObject
)
{
    PDEVICE_CONTEXT deviceContext;

    PAGED_CODE();

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Entry");

    deviceContext = DeviceGetContext(DeviceObject);

    if (deviceContext->VhfHandle != WDF_NO_HANDLE)
    {
        VhfDelete(deviceContext->VhfHandle, TRUE);
    }

}

Menginstal driver sumber HID

Dalam file INF yang menginstal driver sumber HID, pastikan Anda mendeklarasikan Vhf.sys sebagai driver filter yang lebih rendah ke driver sumber HID Anda dengan menggunakan AddReg Directive.

[HIDVHF_Inst.NT.HW]
AddReg = HIDVHF_Inst.NT.AddReg

[HIDVHF_Inst.NT.AddReg]
HKR,,"LowerFilters",0x00010000,"vhf"