Menulis driver klien UCSI

Driver USB Type-C Connector System Software Interface (UCSI) berfungsi sebagai driver pengontrol untuk sistem USB Type-C dengan pengontrol tersemat (EC).

Jika sistem Anda yang menerapkan Platform Policy Manager (PPM), seperti yang dijelaskan dalam spesifikasi UCSI, dalam EC yang terhubung ke sistem melalui:

  • Transportasi ACPI, Anda tidak perlu menulis driver. Muat driver dalam kotak yang disediakan Microsoft, (UcmUcsiCx.sys dan UcmUcsiAcpiClient.sys). (Lihat driver UCSI).

  • Transportasi non-ACPI, seperti USB, PCI, I2C atau UART, Anda perlu menulis driver klien untuk pengontrol.

Catatan

Jika perangkat keras USB Type-C Anda tidak memiliki kemampuan untuk menangani mesin status pengiriman daya (PD), pertimbangkan untuk menulis driver pengontrol port USB Type-C. Untuk informasi selengkapnya, lihat Menulis driver pengontrol port USB Type-C.

Mulai Windows 10, versi 1809, ekstensi kelas baru untuk UCSI (UcmUcsiCx.sys) telah ditambahkan, yang mengimplementasikan spesifikasi UCSI dengan cara agnostik transportasi. Dengan jumlah kode minimal, driver Anda, yang merupakan klien UcmUcsiCx, dapat berkomunikasi dengan perangkat keras USB Type-C melalui transportasi non-ACPI. Topik ini menjelaskan layanan yang disediakan oleh ekstensi kelas UCSI dan perilaku driver klien yang diharapkan.

Spesifikasi resmi

Berlaku untuk:

  • Windows 10, version 1809

Versi WDF

  • KMDF versi 1.27

API penting

Referensi ekstensi kelas UcmUcsiCx

Sampel

Sampel driver klien UcmUcsiCx

Ganti bagian ACPI dengan implementasi Anda untuk bus yang diperlukan.

Arsitektur ekstensi kelas UCSI

Ekstensi kelas UCSI, UcmUcsiCx, memungkinkan Anda menulis driver yang berkomunikasi dengan pengontrol tersematnya dengan menggunakan transportasi non-ACPI. Driver pengontrol adalah driver klien untuk UcmUcsiCx. UcmUcsiCx pada gilirannya adalah klien ke manajer konektor USB (UCM). Oleh karena itu, UcmUcsiCx tidak membuat keputusan kebijakan sendiri. Sebaliknya, ini menerapkan kebijakan yang disediakan oleh UCM. UcmUcsiCx mengimplementasikan mesin status untuk menangani pemberitahuan Platform Policy Manager (PPM) dari driver klien dan mengirim perintah untuk menerapkan keputusan kebijakan UCM, memungkinkan deteksi masalah yang lebih andal dan penanganan kesalahan.

Arsitektur ekstensi kelas UCSI.

Manajer Kebijakan OS (OPM)

OS Policy Manager (OPM) mengimplementasikan logika untuk berinteraksi dengan PPM, seperti yang dijelaskan dalam spesifikasi UCSI. OPM bertanggung jawab untuk:

  • Mengonversi kebijakan UCM menjadi perintah UCSI dan pemberitahuan UCSI menjadi pemberitahuan UCM.
  • Mengirim perintah UCSI yang diperlukan untuk menginisialisasi PPM, mendeteksi kesalahan, dan mekanisme pemulihan.

Menangani perintah UCSI

Operasi umum melibatkan beberapa perintah untuk diselesaikan oleh perangkat keras yang sesuai dengan UCSI. Misalnya, mari kita pertimbangkan perintah GET_CONNECTOR_STATUS.

  1. Firmware PPM mengirimkan pemberitahuan perubahan koneksi ke driver UcmUcsiCx/klien.
  2. Sebagai tanggapan, driver klien UcmUcsiCx/mengirim perintah GET_CONNECTOR_STATUS kembali ke firmware PPM.
  3. Firmware PPM menjalankan GET_CONNECTOR_STATUS dan secara asinkron mengirimkan pemberitahuan lengkap perintah ke driver UcmUcsiCx/klien. Pemberitahuan tersebut berisi data tentang status koneksi aktual.
  4. Driver UcmUcsiCx/klien memproses informasi status tersebut dan mengirim ACK_CC_CI ke firmware PPM.
  5. Firmware PPM menjalankan ACK_CC_CI dan secara asinkron mengirimkan pemberitahuan lengkap perintah ke driver UcmUcsiCx/klien.
  6. Driver UcmUcsiCx/client menganggap perintah GET_CONNECTOR_STATUS selesai.

Komunikasi dengan Platform Policy Manager (PPM)

UcmUcsiCx mengabstraksi detail pengiriman perintah UCSI dari OPM ke firmware PPM dan menerima pemberitahuan dari firmware PPM. Ini mengonversi perintah PPM ke objek WDFREQUEST dan meneruskannya ke driver klien.

  • Pemberitahuan PPM

    Driver klien memberi tahu UcmUcsiCx tentang pemberitahuan PPM dari firmware. Driver menyediakan blok data UCSI yang berisi CCI. UcmUcsiCx meneruskan pemberitahuan ke OPM dan komponen lain yang mengambil tindakan yang sesuai berdasarkan data.

  • IOCTL ke driver klien

    UcmUcsiCx mengirim perintah UCSI (melalui permintaan IOCTL) ke driver klien untuk dikirim ke firmware PPM. Driver bertanggung jawab untuk menyelesaikan permintaan setelah mengirim perintah UCSI ke firmware.

Menangani transisi daya

Driver klien adalah pemilik kebijakan daya.

Jika driver klien memasuki status Dx karena S0-Idle, WDF membawa driver ke D0 ketika UcmUcsiCx mengirim IOCTL yang berisi perintah UCSI ke antrean yang dikelola daya driver klien. Driver klien di S0-Idle harus memasukkan kembali status bertenaga ketika ada pemberitahuan PPM dari firmware karena di S0-Idle, pemberitahuan PPM masih diaktifkan.

Sebelum Anda mulai

  • Tentukan jenis driver yang perlu Anda tulis tergantung pada apakah perangkat keras atau firmware Anda mengimplementasikan mesin status PD, dan transportasi.

    Keputusan untuk memilih ekstensi kelas yang benar. Untuk informasi selengkapnya, lihat Mengembangkan driver Windows untuk konektor USB Type-C.

  • Instal Windows 10 untuk edisi desktop (Home, Pro, Enterprise, dan Education).

  • Instal Windows Driver Kit (WDK) terbaru di komputer pengembangan Anda. Kit ini memiliki file header dan pustaka yang diperlukan untuk menulis driver klien, khususnya, Anda akan memerlukan:

    • Pustaka stub, (UcmUcsiCxStub.lib). Pustaka menerjemahkan panggilan yang dilakukan oleh driver klien dan meneruskannya ke ekstensi kelas.
    • File header, Ucmucsicx.h.
    • Driver klien berjalan dalam mode kernel dan mengikat ke pustaka KMDF 1.27.
  • Biasakan diri Anda dengan Windows Driver Foundation (WDF). Bacaan yang direkomendasikan: Mengembangkan Driver dengan Windows Driver Foundation yang ditulis oleh Penny Orwick dan Guy Smith.

1. Daftarkan driver klien Anda dengan UcmUcsiCx

Dalam implementasi EVT_WDF_DRIVER_DEVICE_ADD Anda.

  1. Setelah Anda mengatur fungsi panggilan balik peristiwa Plug and Play dan manajemen daya (WdfDeviceInitSetPnpowerEventCallbacks), panggil UcmUcsiDeviceInitInitialize untuk menginisialisasi struktur buram WDFDEVICE_INIT. Panggilan mengaitkan driver klien dengan kerangka kerja.

  2. Setelah membuat objek perangkat kerangka kerja (WDFDEVICE), panggil UcmUcsiDeviceInitialize untuk mendaftarkan penyelam klien dengan UcmUcsiCx.

2. Buat objek PPM dengan UcmUcsiCx

Dalam implementasi EVT_WDF_DEVICE_PREPARE_HARDWARE Anda, setelah Anda menerima daftar sumber daya mentah dan terjemahan, gunakan sumber daya untuk menyiapkan perangkat keras. Misalnya, jika transportasi Anda adalah I2C, baca sumber daya perangkat keras untuk membuka saluran komunikasi. Selanjutnya, buat objek PPM. Untuk membuat objek, Anda perlu mengatur opsi konfigurasi tertentu.

  1. Berikan handel ke koleksi konektor pada perangkat.

    1. Buat koleksi konektor dengan memanggil UcmUcsiConnectorCollectionCreate.

    2. Menghitung konektor pada perangkat dan menambahkannya ke koleksi dengan memanggil UcmUcsiConnectorCollectionAddConnector

      // Create the connector collection.
      
      UCMUCSI_CONNECTOR_COLLECTION* ConnectorCollectionHandle;
      
      status = UcmUcsiConnectorCollectionCreate(Device, //WDFDevice
               WDF_NO_OBJECT_ATTRIBUTES,
               ConnectorCollectionHandle);
      
      // Enumerate the connectors on the device.
      // ConnectorId of 0 is reserved for the parent device.
      // In this example, we assume the parent has no children connectors.
      
      UCMUCSI_CONNECTOR_INFO_INIT(&connectorInfo);
      connectorInfo.ConnectorId = 0;
      
      status = UcmUcsiConnectorCollectionAddConnector ( &ConnectorCollectionHandle,
                   &connectorInfo);
      
  2. Tentukan apakah Anda ingin mengaktifkan pengontrol perangkat.

  3. Konfigurasikan dan buat objek PPM.

    1. Inisialisasi struktur UCMUCSI_PPM_CONFIG dengan menyediakan handel konektor yang Anda buat di langkah 1.

    2. Atur anggota UsbDeviceControllerEnabled ke nilai boolean yang ditentukan di langkah 2.

    3. Atur panggilan balik peristiwa Anda di WDF_OBJECT_ATTRIBUTES.

    4. Panggil UcmUcsiPpmCreate dengan meneruskan semua struktur yang dikonfigurasi.

      UCMUCSIPPM ppmObject = WDF_NO_HANDLE;
      PUCMUCSI_PPM_CONFIG UcsiPpmConfig;
      WDF_OBJECT_ATTRIBUTES attrib;
      
      UCMUCSI_PPM_CONFIG_INIT(UcsiPpmConfig, ConnectorCollectionHandle);
      
      UcsiPpmConfig->UsbDeviceControllerEnabled = TRUE;
      
      WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attrib, Ppm);
      attrib->EvtDestroyCallback = &EvtObjectContextDestroy;
      
      status = UcmUcsiPpmCreate(wdfDevice, UcsiPpmConfig, &attrib, &ppmObject);
      

3. Menyiapkan antrean IO

UcmUcsiCx mengirimkan perintah UCSI ke driver klien untuk dikirim ke firmware PPM. Perintah dikirim dalam bentuk permintaan IOCTL ini dalam antrean WDF.

Driver klien bertanggung jawab untuk membuat dan mendaftarkan antrean tersebut ke UcmUcsiCx dengan memanggil UcmUcsiPpmSetUcsiCommandRequestQueue. Antrean harus dikelola daya.

UcmUcsiCx menjamin bahwa paling banyak ada satu permintaan yang luar biasa dalam antrean WDF. Driver klien juga bertanggung jawab untuk menyelesaikan permintaan WDF setelah mengirim perintah UCSI ke firmware.

Biasanya driver menyiapkan antrean dalam implementasi EVT_WDF_DEVICE_PREPARE_HARDWARE.

WDFQUEUE UcsiCommandRequestQueue = WDF_NO_HANDLE;
WDF_OBJECT_ATTRIBUTES attrib;
WDF_IO_QUEUE_CONFIG queueConfig;

WDF_OBJECT_ATTRIBUTES_INIT(&attrib);
attrib.ParentObject = GetObjectHandle();

// In this example, even though the driver creates a sequential queue,
// UcmUcsiCx guarantees that will not send another request
// until the previous one has been completed.


WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchSequential);

// The queue must be power-managed.

queueConfig.PowerManaged = WdfTrue;
queueConfig.EvtIoDeviceControl = EvtIoDeviceControl;

status = WdfIoQueueCreate(device, &queueConfig, &attrib, &UcsiCommandRequestQueue);

UcmUcsiPpmSetUcsiCommandRequestQueue(ppmObject, UcsiCommandRequestQueue);

Selain itu, driver klien juga harus memanggil UcmUcsiPpmStart untuk memberi tahu UcmUcsiCx bahwa driver siap untuk menerima permintaan IOCTL. Kami menyarankan agar Anda melakukan panggilan tersebut di EVT_WDF_DEVICE_PREPARE_HARDWARE setelah membuat handel WDFQUEUE untuk menerima perintah UCSI, melalui UcmUcsiPpmSetUcsiCommandRequestQueue. Sebaliknya, ketika driver tidak ingin memproses permintaan lagi, itu harus memanggil UcmUcsiPpmStop. Lakukan ini ada dalam implementasi EVT_WDF_DEVICE_RELEASE_HARDWARE Anda.

4. Menangani permintaan IOCTL

Pertimbangkan urutan contoh peristiwa yang terjadi saat mitra USB Type-C dilampirkan ke konektor.

  1. Firmware PPM menentukan peristiwa lampiran dan mengirim pemberitahuan ke driver klien.
  2. Driver klien memanggil UcmUcsiPpmNotification untuk mengirim pemberitahuan tersebut ke UcmUcsiCx.
  3. UcmUcsiCx memberi tahu mesin status OPM dan mengirimkan perintah Dapatkan Status Konektor ke UcmUcsiCx.
  4. UcmUcsiCx membuat permintaan dan mengirim IOCTL_UCMUCSI_PPM_SEND_UCSI_DATA_BLOCK ke driver klien.
  5. Driver klien memproses permintaan tersebut dan mengirim perintah ke firmware PPM. Driver menyelesaikan permintaan ini secara asinkron dan mengirim pemberitahuan lain ke UcmUcsiCx.
  6. Pada pemberitahuan lengkap perintah yang berhasil, mesin status OPM membaca payload (berisi info status konektor) dan memberi tahu UCM tentang peristiwa lampiran Type-C.

Dalam contoh ini, payload juga menunjukkan bahwa perubahan status negosiasi pengiriman daya antara firmware dan mitra port berhasil. Mesin status OPM mengirimkan perintah UCSI lain: Dapatkan PDO. Mirip dengan perintah Dapatkan Status Konektor, ketika perintah Dapatkan PDO berhasil diselesaikan, mesin status OPM memberi tahu UCM tentang peristiwa ini.

Handler driver klien untuk EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL mirip dengan kode contoh ini. Untuk informasi tentang menangani permintaan, lihat Penangan Permintaan

void EvtIoDeviceControl(
    _In_ WDFREQUEST Request,
    _In_ ULONG IoControlCode
    )
{
...
    switch (IoControlCode)
    {
    case IOCTL_UCMUCSI_PPM_SEND_UCSI_DATA_BLOCK:
        EvtSendData(Request);
        break;

    case IOCTL_UCMUCSI_PPM_GET_UCSI_DATA_BLOCK:
        EvtReceiveData(Request);
        break;

    default:
        status = STATUS_NOT_SUPPORTED;
        goto Exit;
    }

    status = STATUS_SUCCESS;

Exit:

    if (!NT_SUCCESS(status))
    {
        WdfRequestComplete(Request, status);
    }

}

VOID EvtSendData(
    WDFREQUEST Request
    )
{
    NTSTATUS status;
    PUCMUCSI_PPM_SEND_UCSI_DATA_BLOCK_IN_PARAMS inParams;

    status = WdfRequestRetrieveInputBuffer(Request, sizeof(*inParams),
        reinterpret_cast<PVOID*>(&inParams), nullptr);
    if (!NT_SUCCESS(status))
    {
        goto Exit;
    }

    // Build a UCSI command request and send to the PPM firmware.

Exit:
    WdfRequestComplete(Request, status);
}

VOID EvtReceiveData(
    WDFREQUEST Request
    )
{

    NTSTATUS status;

    PUCMUCSI_PPM_GET_UCSI_DATA_BLOCK_IN_PARAMS inParams;
    PUCMUCSI_PPM_GET_UCSI_DATA_BLOCK_OUT_PARAMS outParams;

    status = WdfRequestRetrieveInputBuffer(Request, sizeof(*inParams),
        reinterpret_cast<PVOID*>(&inParams), nullptr);
    if (!NT_SUCCESS(status))
    {
        goto Exit;
    }

    status = WdfRequestRetrieveOutputBuffer(Request, sizeof(*outParams),
        reinterpret_cast<PVOID*>(&outParams), nullptr);
    if (!NT_SUCCESS(status))
    {
        goto Exit;
    }

    // Receive data from the PPM firmware.
    if (!NT_SUCCESS(status))
    {
        goto Exit;
    }
    WdfRequestSetInformation(Request, sizeof(*outParams));

Exit:
    WdfRequestComplete(Request, status);
}