Mengembangkan driver klien UCSI

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

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

  • Transportasi ACPI, Anda tidak perlu menulis pengandar. Muat driver bawaan 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.

Nota

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 untuk menulis driver pengontrol port USB Type-C.

Mulai dari Windows 10, versi 1809, ekstensi kelas baru untuk UCSI (UcmUcsiCx.sys) telah ditambahkan, yang mengimplementasikan spesifikasi UCSI dengan cara independen dari jenis 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, versi 1809

Versi WDF

  • KMDF versi 1.27

API penting

referensi perpanjangan kelas UcmUcsiCx

Contoh

contoh driver klien UcmUcsiCx

Ganti komponen ACPI dengan implementasi Anda untuk bus yang diperlukan.

Arsitektur ekstensi kelas UCSI

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

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 kompatibel dengan UCSI. Misalnya, mari kita pertimbangkan perintah GET_CONNECTOR_STATUS.

  1. Firmware PPM mengirimkan pemberitahuan perubahan koneksi ke driver UcmUcsiCx/klien.
  2. Sebagai respons, driver UcmUcsiCx/client mengirimkan perintah GET_CONNECTOR_STATUS kembali ke firmware PPM.
  3. Firmware PPM menjalankan GET_CONNECTOR_STATUS dan secara asinkron mengirim pemberitahuan lengkap perintah ke driver UcmUcsiCx/klien. Pemberitahuan tersebut berisi data tentang status koneksi aktual.
  4. Driver UcmUcsiCx/client memproses informasi status tersebut dan mengirim ACK_CC_CI ke firmware PPM.
  5. Firmware PPM menjalankan ACK_CC_CI dan secara asinkron mengirim 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 pengelola kebijakan daya.

Jika pengandar klien memasuki status Dx karena S0-Idle, WDF membawa pengandar ke D0 saat UcmUcsiCx mengirimkan IOCTL yang berisi perintah UCSI ke antrean daya yang dikelola oleh pengandar klien. Driver klien di S0-Idle seharusnya kembali ke 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 metode transmisi.

    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). Perpustakaan menerjemahkan panggilan yang dilakukan oleh driver klien dan meneruskannya ke ekstensi kelas.
    • Header file, 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 disarankan: Mengembangkan Driver dengan Windows Driver Foundation karya 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 untuk peristiwa plug and play dan manajemen daya (WdfDeviceInitSetPnpPowerEventCallbacks), 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 driver klien dengan UcmUcsiCx.

2. Buat objek PPM dengan UcmUcsiCx

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

  1. Berikan akses ke koleksi konektor pada perangkat.

    1. Buat koleksi konektor dengan memanggil UcmUcsiConnectorCollectionBuat.

    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 callback event Anda di WDF_OBJECT_ATTRIBUTES.

    4. Panggil UcmUcsiPpmCreate dengan melewati 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 dalam bentuk permintaan IOCTL ini dikirim dalam antrean WDF.

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

UcmUcsiCx menjamin bahwa hanya ada satu permintaan tertunda dalam antrean WDF. Driver klien juga bertanggung jawab untuk menyelesaikan permintaan WDF setelah mengirim perintah UCSI ke firmware.

Biasanya, driver mengantrekan perangkat dalam implementasinya untuk 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 handle WDFQUEUE untuk menerima perintah UCSI, melalui UcmUcsiPpmSetUcsiCommandRequestQueue. Sebaliknya, ketika driver tidak ingin memproses permintaan lagi, driver harus memanggil UcmUcsiPpmStop. Lakukan ini dalam implementasi EVT_WDF_DEVICE_RELEASE_HARDWARE Anda.

4. Tangani 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 komputer 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 selesai perintah yang berhasil, mesin status OPM membaca payload (yang berisi informasi status konektor) dan memberi tahu UCM tentang peristiwa penyambungan 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: Ambil 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 Request Handlers

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