Condividi tramite


Scrivere un driver client UCSI

Un driver USB Type-C Connector 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). Consultare il driver UCSI ().

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

Nota

Se l'hardware USB Type-C non ha la capacità di gestire lo stato di consegna elettrica (PD), è consigliabile scrivere un driver per il 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 funge da client per UcmUcsiCx, può comunicare con l'hardware USB Type-C attraverso un 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

riferimento alle estensioni della classe UcmUcsiCx

Esempio

esempio di driver client UcmUcsiCx

Sostituire le sezioni ACPI con la tua 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 conforme a UCSI. 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 comando GET_CONNECTOR_STATUS sia completo.

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. Trasforma i comandi PPM in oggetti WDFREQUEST e li inoltra al driver del 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 del 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 responsabile della politica di gestione dell'alimentazione.

Se il driver del 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 dall'alimentazione del driver del client. Il driver client in S0-Idle dovrebbe ritornare a uno stato attivo quando c'è una notifica PPM dal firmware perché in S0-Idle, le notifiche PPM sono ancora abilitate.

Prima di iniziare

  • Determinare il tipo di driver che è necessario scrivere in base al fatto 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:

    • La libreria stub, (UcmUcsiCxStub.lib). La libreria converte le chiamate effettuate dal driver client e le passa all'estensione della classe.
    • Il 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 di 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), chiamare UcmUcsiDeviceInitialize per registrare il client diver 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, usa le risorse per preparare l'hardware. Ad esempio, se il protocollo è I2C, leggi 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 un'interfaccia alla raccolta di connettori sul dispositivo.

    1. Creare la raccolta di connettori chiamando UcmUcsiConnectorCollectionCreare.

    2. Enumerare i connettori del dispositivo e aggiungerli alla raccolta chiamando 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. Decidere se si vuole abilitare il controller del dispositivo.

  3. Configurare e creare l'oggetto PPM.

    1. Inizializza una struttura UCMUCSI_PPM_CONFIG fornendo l'handle del connettore che hai creato nel passaggio 1.

    2. Imposta il membro UsbDeviceControllerEnabled su un valore booleano determinato al passaggio 2.

    3. Imposta i callback degli eventi in WDF_OBJECT_ATTRIBUTES.

    4. Chiamare UcmUcsiPpmCrea 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 di tale coda in UcmUcsiCx chiamando UcmUcsiPpmSetUcsiCommandRequestQueue. La coda deve essere gestita dal sistema di gestione energetica.

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. È consigliato eseguire quella chiamata in EVT_WDF_DEVICE_PREPARE_HARDWARE dopo aver creato l'handle WDFQUEUE per ricevere i comandi UCSI, attraverso UcmUcsiPpmSetUcsiCommandRequestQueue. Viceversa, quando il driver non desidera elaborare ulteriori richieste, deve invocare UcmUcsiPpmStop. Esegui questa operazione nell'implementazione EVT_WDF_DEVICE_RELEASE_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 Connector Status a UcmUcsiCx.
  4. UcmUcsiCx crea una richiesta e invia IOCTL_UCMUCSI_PPM_SEND_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 successo della notifica di completamento del comando, la macchina a stati OPM legge il payload (contenente le informazioni sullo stato del connettore) e notifica l'evento di collegamento Type-C a UCM.

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: Ottieni PDOs. Analogamente al comando Get Connector Status (Ottieni stato connettore), quando il comando Get PDOs viene completato correttamente, la macchina a stati OPM notifica questo evento a UCM.

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