Condividi tramite


Scrivere un driver client UCSI

Un driver USB Type-C Connessione or System Software Interface (UCSI) funge da driver controller per un sistema USB Type-C con un controller incorporato (EC).

Se il sistema che implementa Platform Policy Manager (PPM), come descritto nella specifica UCSI, in una EC connessa al sistema tramite:

  • Un trasporto ACPI non è necessario scrivere un driver. Caricare il driver integrato fornito da Microsoft (UcmUcsiCx.sys e UcmUcsiAcpiClient.sys). (Vedere Driver UCSI).

  • Un trasporto non ACPI, ad esempio USB, PCI, I2C o UART, è necessario scrivere un driver client per il controller.

Nota

Se l'hardware USB Type-C non ha la capacità di gestire la macchina a stati di alimentazione (PD), prendere in considerazione la scrittura di un driver del controller di porta USB Type-C. Per altre informazioni, vedere Scrivere un driver del controller di porta USB Type-C.

A partire da Windows 10, versione 1809, è stata aggiunta una nuova estensione di classe per UCSI (UcmUcsiCx.sys), che implementa la specifica UCSI in modo indipendente dal trasporto. Con una quantità minima di codice, il driver, che è un client a UcmUcsiCx, può comunicare con l'hardware USB Type-C su trasporto non ACPI. In questo argomento vengono descritti i servizi forniti dall'estensione della classe UCSI e il comportamento previsto del driver client.

Specifiche ufficiali

Si applica a:

  • Windows 10, versione 1809

Versione di WDF

  • KMDF versione 1.27

API importanti

Informazioni di riferimento sulle estensioni della classe UcmUcsiCx

Esempio

Esempio di driver client UcmUcsiCx

Sostituire le parti ACPI con l'implementazione per il bus richiesto.

Architettura dell'estensione della classe UCSI

L'estensione della classe UCSI, UcmUcsiCx, consente di scrivere un driver che comunica con il controller incorporato usando il trasporto non ACPI. Il driver del controller è un driver client per UcmUcsiCx. UcmUcsiCx è a sua volta un client per la gestione dei connettori USB( UCM). Di conseguenza, UcmUcsiCx non prende decisioni politiche proprie. Implementa invece i criteri forniti da UCM. UcmUcsiCx implementa le macchine a stati per la gestione delle notifiche di Platform Policy Manager (PPM) dal driver client e invia comandi per implementare decisioni relative ai criteri UCM, consentendo un rilevamento dei problemi e una gestione degli errori più affidabile.

Architettura dell'estensione della classe UCSI.

Os Policy Manager (OPM)

Os Policy Manager (OPM) implementa la logica per interagire con PPM, come descritto nella specifica UCSI. OPM è responsabile di:

  • Conversione dei criteri UCM in comandi UCSI e notifiche UCSI in notifiche UCM.
  • Invio di comandi UCSI necessari per inizializzare PPM, rilevare gli errori e i meccanismi di ripristino.

Gestione dei comandi UCSI

Un'operazione tipica prevede diversi comandi da completare dall'hardware ucsi-complicante. Si consideri ad esempio il comando GET_CONNECTOR_STATUS.

  1. Il firmware PPM invia una notifica di modifica della connessione al driver UcmUcsiCx/client.
  2. In risposta, il driver UcmUcsiCx/client invia un comando GET_CONNECTOR_STATUS al firmware PPM.
  3. Il firmware PPM esegue GET_CONNECTOR_STATUS e invia in modo asincrono una notifica di completamento del comando al driver UcmUcsiCx/client. Tale notifica contiene dati sullo stato effettivo della connessione.
  4. Il driver UcmUcsiCx/client elabora le informazioni sullo stato e invia un ACK_CC_CI al firmware PPM.
  5. Il firmware PPM esegue ACK_CC_CI e invia in modo asincrono una notifica di completamento del comando al driver UcmUcsiCx/client.
  6. Il driver UcmUcsiCx/client considera il GET_CONNECTOR_STATUS comando da completare.

Comunicazione con Platform Policy Manager (PPM)

UcmUcsiCx astrae i dettagli dell'invio di comandi UCSI da OPM al firmware PPM e la ricezione di notifiche dal firmware PPM. Converte i comandi PPM in oggetti WDFREQUEST e li inoltra al driver client.

  • Notifiche PPM

    Il driver client notifica a UcmUcsiCx le notifiche PPM dal firmware. Il driver fornisce il blocco di dati UCSI contenente CCI. UcmUcsiCx inoltra le notifiche a OPM e ad altri componenti che eseguono azioni appropriate in base ai dati.

  • IOCTLs al driver client

    UcmUcsiCx invia comandi UCSI (tramite richieste IOCTL) al driver client da inviare al firmware PPM. Il driver è responsabile del completamento della richiesta dopo l'invio del comando UCSI al firmware.

Gestione delle transizioni di alimentazione

Il driver client è il proprietario dei criteri di alimentazione.

Se il driver client entra in uno stato Dx a causa di S0-Idle, WDF porta il driver a D0 quando UcmUcsiCx invia un IOCTL contenente un comando UCSI alla coda gestita dal driver client. Il driver client in S0-Idle dovrebbe reinserire uno stato alimentato quando è presente una notifica PPM dal firmware perché in S0-Idle, le notifiche PPM sono ancora abilitate.

Operazioni preliminari

  • Determinare il tipo di driver che è necessario scrivere a seconda che l'hardware o il firmware implementi la macchina a stati PD e il trasporto.

    Decisione per la scelta dell'estensione della classe corretta. Per altre informazioni, vedere Sviluppo di driver Windows per connettori USB Type-C.

  • Installare Windows 10 per le edizioni desktop (Home, Pro, Enterprise ed Education).

  • Installare la versione più recente di Windows Driver Kit (WDK) nel computer di sviluppo. Il kit include i file di intestazione e le librerie necessari per scrivere il driver client, in particolare, è necessario:

    • Libreria stub, (UcmUcsiCxStub.lib). La libreria converte le chiamate effettuate dal driver client e le passa all'estensione della classe.
    • File di intestazione Ucmucsicx.h.
    • Il driver client viene eseguito in modalità kernel e viene associato alla libreria KMDF 1.27.
  • Acquisire familiarità con Windows Driver Foundation (WDF). Lettura consigliata: Sviluppo di driver con Windows Driver Foundation scritto da Penny Orwick e Guy Smith.

1. Registrare il driver client con UcmUcsiCx

Nell'implementazione EVT_WDF_DRIVER_DEVICE_ADD .

  1. Dopo aver impostato le funzioni di callback degli eventi plug and play e risparmio energia (WdfDeviceInitSetPnpPowerEventCallbacks), chiamare UcmUcsiDeviceInitInitialize per inizializzare la struttura opaca WDFDEVICE_INIT. La chiamata associa il driver client al framework.

  2. Dopo aver creato l'oggetto dispositivo framework (WDFDEVICE), chiama UcmUcsiDeviceInitialize per registrare il subacqueo client con UcmUcsiCx.

2. Creare l'oggetto PPM con UcmUcsiCx

Nell'implementazione di EVT_WDF_DEVICE_PREPARE_HARDWARE, dopo aver ricevuto l'elenco delle risorse non elaborate e tradotte, usare le risorse per preparare l'hardware. Ad esempio, se il trasporto è I2C, leggere le risorse hardware per aprire un canale di comunicazione. Creare quindi un oggetto PPM. Per creare l'oggetto, è necessario impostare determinate opzioni di configurazione.

  1. Fornire un handle alla raccolta di connettori nel dispositivo.

    1. Creare la raccolta di connettori chiamando UcmUcsi Connessione orCollectionCreate.

    2. Enumerare i connettori nel dispositivo e aggiungerli alla raccolta chiamando UcmUcsi Connessione orCollectionAdd Connessione or

      // 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. Decidere se si vuole abilitare il controller del dispositivo.

  3. Configurare e creare l'oggetto PPM.

    1. Inizializzare una struttura UCMUCSI_PPM_CONFIG specificando l'handle del connettore creato nel passaggio 1.

    2. Impostare UsbDeviceControllerEnabled membro su un valore booleano determinato nel passaggio 2.

    3. Impostare i callback degli eventi in WDF_OBJECT_ATTRIBUTES.

    4. Chiamare UcmUcsiPpmCreate passando tutte le strutture configurate.

      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. Configurare le code di I/O

UcmUcsiCx invia comandi UCSI al driver client da inviare al firmware PPM. I comandi vengono inviati sotto forma di queste richieste IOCTL in una coda WDF.

Il driver client è responsabile della creazione e della registrazione della coda in UcmUcsiCx chiamando UcmUcsiPpmSetUcsiCommandRequestQueue. La coda deve essere gestita dall'alimentazione.

UcmUcsiCx garantisce che sia presente al massimo una richiesta in sospeso nella coda WDF. Il driver client è anche responsabile del completamento della richiesta WDF dopo l'invio del comando UCSI al firmware.

In genere il driver configura le code nell'implementazione di 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);

Inoltre, il driver client deve anche chiamare UcmUcsiPpmStart per notificare a UcmUcsiCx che il driver è pronto per ricevere le richieste IOCTL. È consigliabile effettuare tale chiamata in EVT_WDF_DEVICE_PREPARE_HARDWARE dopo aver creato l'handle WDFQUEUE per la ricezione di comandi UCSI tramite UcmUcsiPpmSetUcsiCommandRequestQueue. Viceversa, quando il driver non vuole elaborare altre richieste, deve chiamare UcmUcsiPpmStop. Eseguire questa operazione nell'implementazione EVT_WDF_DEVICE_RELEA edizione Standard_HARDWARE.

4. Gestire le richieste IOCTL

Si consideri questa sequenza di esempio degli eventi che si verificano quando un partner USB Type-C è collegato a un connettore.

  1. Il firmware PPM determina un evento di collegamento e invia una notifica al driver client.
  2. Il driver client chiama UcmUcsiPpmNotification per inviare tale notifica a UcmUcsiCx.
  3. UcmUcsiCx notifica alla macchina a stati OPM e invia un comando Get Connessione or Status a UcmUcsiCx.
  4. UcmUcsiCx crea una richiesta e invia IOCTL_UCMUCSI_PPM_edizione Standard ND_UCSI_DATA_BLOCK al driver client.
  5. Il driver client elabora la richiesta e invia il comando al firmware PPM. Il driver completa questa richiesta in modo asincrono e invia un'altra notifica a UcmUcsiCx.
  6. Al termine della notifica del comando, la macchina a stati OPM legge il payload (contenente le informazioni sullo stato del connettore) e notifica a UCM dell'evento di collegamento Type-C.

In questo esempio, il payload ha anche indicato che una modifica dello stato di negoziazione della distribuzione dell'alimentazione tra il firmware e il partner della porta ha avuto esito positivo. La macchina a stati OPM invia un altro comando UCSI: Get PDOs.The OPM state machine invia un altro comando UCSI: Get PDOs. Analogamente al comando Ottieni stato Connessione or, quando il comando Get PDOs viene completato correttamente, la macchina a stati OPM notifica a UCM di questo evento.

Il gestore del driver client per EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL è simile a questo codice di esempio. Per informazioni sulla gestione delle richieste, vedere Gestori delle richieste

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