Schreiben eines USB-Typ-C-Portcontrollertreibers

Sie müssen einen USB-Typ-C-Portcontrollertreiber schreiben, wenn Ihre USB-Typ-C-Hardware die physische USB-Typ-C- oder Power Delivery-Schicht (PD) implementiert, aber nicht die zustandsgesteuerten Computer implementiert, die für power delivery erforderlich sind.

In Windows 10 Version 1703 wurde die USB Typ-C-Architektur verbessert, um Hardwaredesigns zu unterstützen, die die physische USB-Typ-C- oder Power Delivery-Schicht (PD) implementieren, aber keine entsprechende PD-Richtlinien-Engine oder Protokollebenenimplementierung haben. Für diese Designs bietet Windows 10 Version 1703 eine softwarebasierte PD-Richtlinien-Engine und einen Geräterichtlinien-Manager über eine neue Klassenerweiterung namens "USB Connector Manager Typ-C Port Controller Interface Class Extension" (UcmTcpciCx). Ein von einem IHV oder OEM/ODM geschriebener Clienttreiber kommuniziert mit UcmTcpciCx, um Informationen zu den Hardwareereignissen bereitzustellen, die für die Funktion der PD-Richtlinien-Engine und des Geräterichtlinien-Managers in UcmTcpciCx erforderlich sind. Diese Kommunikation wird über eine Reihe von Programmierschnittstellen ermöglicht, die in diesem Artikel und im Referenzabschnitt beschrieben werden.

Diagramm des USB-Connector-Managers.

Die UcmTcpciCx-Klassenerweiterung ist selbst ein Clienttreiber von UcmCx. Die Richtlinienentscheidungen zu Energieverträgen und Datenrollen werden in UcmCx getroffen und an UcmTcpciCx weitergeleitet. UcmTcpciCx implementiert diese Richtlinien und verwaltet die Zustandscomputer Typ-C und PD mithilfe der Portcontrollerschnittstelle, die von Ihrem UcmTcpciCx-Clienttreiber bereitgestellt wird.

Zusammenfassung

  • Von der UcmTcpci-Klassenerweiterung bereitgestellte Dienste
  • Erwartetes Verhalten des Clienttreibers

Offizielle Spezifikationen

Wichtige APIs

Referenz zu USB Type-C Port Controller Interface-Treiberklassenerweiterungen

UcmTcpciCx-Clienttreibervorlage

UcmTcpciCx-Clienttreibervorlage

Voraussetzungen

  • Bestimmen Sie den Typ des Treibers, den Sie schreiben müssen, je nachdem, ob Ihre Hardware oder Firmware den PD-Zustandscomputer implementiert. Weitere Informationen finden Sie unter Entwickeln von Windows-Treibern für USB-Typ-C-Anschlüsse.

  • Installieren Sie Windows 10 für Desktopeditionen (Home, Pro, Enterprise und Education) auf Ihrem Zielcomputer oder Windows 10 Mobile mit einem USB-Typ-C-Anschluss.

  • Installieren Sie das neueste Windows Driver Kit (WDK) auf Ihrem Entwicklungscomputer. Das Kit verfügt über die erforderlichen Headerdateien und Bibliotheken zum Schreiben des Clienttreibers, insbesondere benötigen Sie Folgendes:

    • Die Stubbibliothek (UcmTcpciCxStub.lib). Die Bibliothek übersetzt Aufrufe des Clienttreibers und übergibt sie an die Klassenerweiterung.
    • Die Headerdatei UcmTcpciCx.h.

    Der Clienttreiber wird im Kernelmodus ausgeführt und an die KMDF 1.15-Bibliothek gebunden.

    Screenshot der Visual Studio-Konfiguration für UCM.

  • Entscheiden Sie, ob der Clienttreiber Warnungen unterstützt.

  • Ihr Portcontroller muss nicht TCPCI-kompatibel sein. Die -Schnittstelle erfasst die Funktionen jedes Typ-C-Portcontrollers. Beim Schreiben eines UcmTcpciCx-Clienttreibers für Hardware, die nicht TCPCI-kompatibel ist, müssen die Bedeutungen von Registern und Befehlen in der TCPCI-Spezifikation denen der Hardware zugeordnet werden.

  • Die meisten TCPCI-Controller sind mit I2C verbunden. Ihr Clienttreiber verwendet eine SPB-Verbindungsressource (Serial Peripheral Bus) und eine Interruptleitung, um mit der Hardware zu kommunizieren. Der Treiber verwendet die SpbCx-Programmierschnittstelle (SPB Framework Extension). Machen Sie sich mit SpbCx vertraut, indem Sie diese Artikel lesen:

    • [SpB-Treiberdesignhandbuch ( Simple Peripheral Bus) ]
    • [SPB-Treiberprogrammierungsreferenz]
  • Machen Sie sich mit Windows Driver Foundation (WDF) vertraut. Empfohlene Lektüre: Entwickeln von Treibern mit Windows Driver Foundation, geschrieben von Penny Orwick und Guy Smith.

Verhalten der UcmTcpci-Klassenerweiterung

  • Im Rahmen der Ausführung des Zustandscomputers sendet UcmTcpciCx IOCTL-Anforderungen an den Portcontroller. Beim PD-Messaging wird beispielsweise eine IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_TRANSMIT_BUFFER-Anforderung gesendet, um den Übertragungspuffer festzulegen. Diese Anforderung (TRANSMIT_BUFFER) wird an den Clienttreiber übergeben. Der Treiber legt dann den Übertragungspuffer mit den Details fest, die von der Klassenerweiterung bereitgestellt werden.

  • UcmTcpciCx implementiert Richtlinien zu Energieverträgen, Datenrollen usw.

Erwartetes Verhalten des Clienttreibers

Vom Clienttreiber für ucmTcpciCx wird folgendes erwartet:

  • Sein Sie der Besitzer der Energierichtlinie. UcmTcpciCx ist nicht an der Energieverwaltung des Portcontrollers beteiligt.

  • Übersetzen Von UcmTcpciCx empfangene Anforderungen in hardwarebasierte Lese- oder Schreibbefehle. Die Befehle müssen asynchron sein, da DPM das Warten auf den Abschluss einer Hardwareübertragung nicht blockieren kann.

  • Stellen Sie ein Frameworkwarteschlangenobjekt bereit, das Frameworkanforderungsobjekte enthält. Für jede Anforderung, die die UcmTcpci-Klassenerweiterung an den Clienttreiber senden möchte, fügt die Erweiterung ein Anforderungsobjekt im Warteschlangenobjekt des Treibers hinzu. Wenn der Treiber die Verarbeitung der Anforderung abgeschlossen hat, ruft er WdfRequestComplete auf. Es liegt in der Verantwortung des Clienttreibers, Anforderungen rechtzeitig abzuschließen.

  • Ermitteln und Melden der Funktionen des Portcontrollers. Zu diesen Funktionen gehören Informationen wie die Rollen, in denen der Portcontroller ausgeführt werden kann (z. B. Source-only, Sink-only, DRP). Es gibt jedoch weitere Funktionen des Connectors (siehe Hinweis zum Funktionsspeicher) und des Systems als Ganzes, die der DPM kennen muss, um die USB-Typ-C- und PD-Richtlinie ordnungsgemäß implementieren zu können. Für instance muss der DPM die Quellfunktionen des Systems/Connectors kennen, um es beim Portpartner anzukündigen.

    Funktionsspeicher

    Zusätzlich zu den clienttreiberbezogenen Funktionen stammen zusätzliche Informationen von einem system-globalen Speicherort, der als Funktionsspeicher bezeichnet wird. Dieser system-globale Funktionsspeicher wird in ACPI gespeichert. Es handelt sich um eine statische Beschreibung der Funktionen des Systems und der einzelnen USB-Typ-C-Anschlüsse, die vom DPM zum Bestimmen der zu implementierenden Richtlinien verwendet werden.

    Indem die Beschreibung der Systemfunktionen vom Clienttreiber für die Portcontroller getrennt wird, ermöglicht der Entwurf die Verwendung eines Treibers auf verschiedenen Systemen mit unterschiedlichen Funktionen. UcmCx, nicht UcmTcpciCx, ist eine Schnittstelle mit dem Funktionsspeicher. UcmTcpciCx (oder der zugehörige Clienttreiber) interagiert nicht mit dem Funktionsspeicher.

    Sofern zutreffend, überschreiben die Informationen aus dem Funktionsspeicher Informationen, die direkt vom Portcontrollerclienttreiber stammen. Für instance kann ein Portcontroller nur sinken, und der Clienttreiber meldet diese Informationen. Der Rest des Systems ist jedoch möglicherweise nicht ordnungsgemäß für den Reinsenken-Betrieb konfiguriert. In diesem Fall kann der Systemhersteller melden, dass die Connectors im Funktionsspeicher nur für den Source-Betrieb geeignet sind. Die Einstellung im Funktionsspeicher hat Vorrang vor den vom Treiber gemeldeten Informationen.

  • Benachrichtigen Sie UcmTcpciCx mit allen relevanten Daten im Zusammenhang mit den Warnungen.

  • Optional. Führen Sie zusätzliche Verarbeitungsvorgänge durch, nachdem ein alternativer Modus in den /beendet wurde. Der Treiber wird über diese Zustände durch die Klassenerweiterung über IOCTL-Anforderungen informiert.

Registrieren des Clienttreibers bei UcmTcpciCx

Beispielreferenz: Siehe EvtPrepareHardware in Device.cpp.

  1. Rufen Sie in Ihrer EVT_WDF_DRIVER_DEVICE_ADD Implementierung UcmTcpciDeviceInitInitialize auf, um die WDFDEVICE_INIT undurchsichtige Struktur zu initialisieren. Der Aufruf ordnet den Clienttreiber dem Framework zu.

  2. Rufen Sie nach dem Erstellen des Frameworkgeräteobjekts (WDFDEVICE) UcmTcpciDeviceInitialize auf, um den Client diver bei UcmTcpciCx zu registrieren.

Initialisieren des I2C-Kommunikationskanals mit der Portcontrollerhardware

Beispielreferenz: Siehe EvtCreateDevice in Device.cpp.

Lesen Sie in Ihrer EVT_WDF_DEVICE_PREPARE_HARDWARE Implementierung die Hardwareressourcen, um einen Kommunikationskanal zu öffnen. Dies ist erforderlich, um PD-Funktionen abzurufen und über Warnungen benachrichtigt zu werden.

Die meisten TCPCI-Controller sind mit I2C verbunden. Im Referenzbeispiel öffnet der Clienttreiber mithilfe der SpbCx-Programmierschnittstelle (SPB Framework Extension) einenI2-Kanal .

Der Clienttreiber listet die Hardwareressourcen auf, indem er WdfCmResourceListGetDescriptor aufruft.

Warnungen werden als Unterbrechungen empfangen. Daher erstellt der Treiber ein Framework-Interruptobjekt und registriert die ISR, die die Warnungen verarbeitet. Die ISR führt Hardwarelese- und Schreibvorgänge aus, die blockiert werden, bis der Hardwarezugriff abgeschlossen ist. Da das Warten bei DIRQL inakzeptabel ist, führt der Treiber die ISR bei PASSIVE_LEVEL aus.

Initialisieren der Typ-C- und PD-Funktionen des Portcontrollers

Beispielreferenz: Siehe EvtDeviceD0Entry in Device.cpp.

In Ihrer EVT_WDF_DEVICE_D0_EXIT Implementierung

  1. Kommunizieren Sie mit der Hardware des Portcontrollers, und rufen Sie geräteidentifikation und -funktionen ab, indem Sie verschiedene Register lesen.

  2. Initialisieren Sie UCMTCPCI_PORT_CONTROLLER_IDENTIFICATION und UCMTCPCI_PORT_CONTROLLER_CAPABILITIES mit den abgerufenen Informationen.

  3. Initialisieren Sie UCMTCPCI_PORT_CONTROLLER_CONFIG Struktur mit den vorherigen Informationen, indem Sie die initialisierten Strukturen an UCMTCPCI_PORT_CONTROLLER_CONFIG_INIT übergeben.

  4. Rufen Sie UcmTcpciPortControllerCreate auf, um das Portcontrollerobjekt zu erstellen und das UCMTCPCIPORTCONTROLLER-Handle abzurufen.

Einrichten eines Frameworkwarteschlangenobjekts zum Empfangen von Anforderungen von UcmTcpciCx

Beispielreferenz: Siehe EvtDeviceD0Entry in Device.cpp und HardwareRequestQueueInitialize in Queue.cpp.

  1. Erstellen Sie in Ihrer EVT_WDF_DEVICE_D0_EXIT Implementierung ein Framework-Warteschlangenobjekt, indem Sie WdfIoQueueCreate aufrufen. In diesem Aufruf müssen Sie Ihre Rückrufimplementierung registrieren, um IOCTL-Anforderungen zu verarbeiten, die von UcmTpciCx gesendet werden. Der Clienttreiber kann eine energieverwaltete Warteschlange verwenden.

    Während der Ausführung der Computer Typ-C und PD sendet UcmTpciCx Befehle zur Ausführung an den Clienttreiber. UcmTcpciCx garantiert zu einem bestimmten Zeitpunkt höchstens eine ausstehende Portcontrolleranforderung.

  2. Rufen Sie UcmTcpciPortControllerSetHardwareRequestQueue auf, um das neue Framework-Warteschlangenobjekt bei UcmTpciCx zu registrieren. Nachdem dieser Aufruf erfolgreich war, platziert UcmTcpciCx Framework-Warteschlangenobjekte (WDFREQUEST) in dieser Warteschlange, wenn eine Aktion vom Treiber erforderlich ist.

  3. Implementieren Sie die Rückruffunktion EvtIoDeviceControl, um diese IOCTLs zu verarbeiten.

Steuerungscode BESCHREIBUNG
IOCTL_UCMTCPCI_PORT_CONTROLLER_GET_STATUS Ruft Die Werte aller status Register gemäß der Schnittstellenspezifikation für den Universal Serial Bus Type-C-Port-Controller ab. Der Clienttreiber muss die Werte der CC_STATUS-, POWER_STATUS- und FAULT_STATUS-Register abrufen.
IOCTL_UCMTCPCI_PORT_CONTROLLER_GET_CONTROL Ruft die Werte aller Steuerelementregister ab, die gemäß der Schnittstellenspezifikation für den Universal Serial Bus Type-C port controller definiert sind.
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_CONTROL Legt den Wert eines Steuerelementregisters fest, das gemäß der Schnittstellenspezifikation für den Universal Serial Bus Type-C Port-Controller definiert ist.
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_TRANSMIT Legt das TRANSMIT-Register fest, das gemäß der Controller-Schnittstellenspezifikation für den universellen seriellen Bus typ-C definiert ist.
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_TRANSMIT_BUFFER Legt die TRANSMIT_BUFER Register fest, die gemäß der Schnittstellenspezifikation für controller-Controller vom Typ C des universellen seriellen Busses definiert ist.
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_RECEIVE_DETECT Legt den RECEIVE_DETECT Register fest, der gemäß der Schnittstellenspezifikation für controller-Controller vom Typ C des universellen seriellen Busses definiert ist.
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_CONFIG_STANDARD_OUTPUT Legt die CONFIG_STANDARD_OUTPUT Register fest, die gemäß der Schnittstellenspezifikation für die Controllerschnittstelle vom Typ C des universellen seriellen Busses definiert ist.
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_COMMAND Legt den Wert eines Befehlsregisters fest, das gemäß der Schnittstellenspezifikation für den Universal Serial Bus Type-C Port Controller definiert ist.
IOCTL_UCMTCPCI_PORT_CONTROLLER_SET_MESSAGE_HEADER_INFO Legt den Wert des MESSAGE_HEADER_INFO Register fest, der gemäß der Schnittstellenspezifikation für den Universal Serial Bus Typ-C port controller definiert ist.
IOCTL_UCMTCPCI_PORT_CONTROLLER_ALTERNATE_MODE_ENTERED Benachrichtigt den Clienttreiber, dass ein alternativer Modus eingegeben wird, damit der Treiber andere Aufgaben ausführen kann.
IOCTL_UCMTCPCI_PORT_CONTROLLER_ALTERNATE_MODE_EXITED Benachrichtigt den Clienttreiber, dass ein alternativer Modus beendet wird, damit der Treiber andere Aufgaben ausführen kann.
IOCTL_UCMTCPCI_PORT_CONTROLLER_DISPLAYPORT_CONFIGURED Benachrichtigt den Clienttreiber, dass der alternative DisplayPort-Modus auf dem Partnergerät mit Pinzuweisung konfiguriert wurde, damit der Treiber andere Aufgaben ausführen kann.
IOCTL_UCMTCPCI_PORT_CONTROLLER_DISPLAYPORT_HPD_STATUS_CHANGED Benachrichtigt den Clienttreiber, dass der Hot-Plug erkennt, status der DisplayPort-Verbindung geändert wurde, sodass der Treiber andere Aufgaben ausführen kann.
  1. Rufen Sie UcmTcpciPortControllerStart auf, um UcmTcpciCx anzuweisen, den Portcontroller zu starten. UcmTcpciCx übernimmt die Kontrolle über USB Typ-C und Power Delivery. Nachdem der Portcontroller gestartet wurde, kann UcmTcpciCx damit beginnen, Anforderungen in die Hardwareanforderungswarteschlange zu stellen.

Behandeln von Warnungen von der Portcontrollerhardware

Beispielreferenz: Siehe ProcessAndSendAlerts in Alert.cpp

Der Clienttreiber muss Warnungen (oder Ereignisse) verarbeiten, die von der Portcontrollerhardware empfangen werden, und sie mit Daten im Zusammenhang mit dem Ereignis an UcmTcpciCx senden.

Wenn eine Hardwarewarnung auftritt, treibt die Portcontrollerhardware den ALERT-Pin hoch. Dadurch wird der ISR des Clienttreibers (in Schritt 2 registriert) aufgerufen. Die Routinedienste, die die Hardware bei PASSIVE_LEVEL unterbricht. Die Routine bestimmt, ob es sich bei einem Interrupt um eine Warnung der Portcontrollerhardware handelt. wenn ja, wird die Verarbeitung der Warnung abgeschlossen und UcmTcpciCx benachrichtigt, indem UcmTcpciPortControllerAlert aufgerufen wird.

Vor dem Aufruf von UcmTcpciPortControllerAlert ist der Client dafür verantwortlich, alle relevanten Daten im Zusammenhang mit der Warnung in eine UCMTCPCI_PORT_CONTROLLER_ALERT_DATA-Struktur einzufügen. Der Client stellt ein Array aller aktiven Warnungen bereit, da es möglich ist, dass die Hardware mehrere Warnungen gleichzeitig ausgibt.

Hier sehen Sie einen Beispielfluss von Aufgaben, die Änderungen im CC-Status melden sollen.

  1. Der Client empfängt eine Hardwarewarnung.

  2. Der Client liest das ALERT-Register und bestimmt die typwarnungen, die aktiv sind.

  3. Der Client liest das CC STATUS-Register und beschreibt den Inhalt des CC STATUS-Registers in UCMTCPCI_PORT_CONTROLLER_ALERT_DATA. Der Treiber legt alertType-Member auf UcmTcpciPortControllerAlertCCStatus und CCStatus-Mitglied von register fest.

  4. Der Client ruft UcmPortControllerAlert auf, um die Arrayhardwarewarnungen an UcmTcpciCx zu senden.

  5. Der Client löscht die Warnung (dies kann jederzeit geschehen, nachdem der Client die Warnungsinformationen abgerufen hat).

Verarbeiten von Anforderungen, die von UcmTcpciCx empfangen wurden

Beispielreferenz: Siehe PortControllerInterface.cpp

Im Rahmen der Zustandscomputerausführung muss UcmTcpciCx Anforderungen an den Portcontroller senden. Beispielsweise muss die TRANSMIT_BUFFER festgelegt werden. Diese Anforderung wird an den Clienttreiber übergeben. Der Treiber legt den Übertragungspuffer mit den von UcmTcpciCx bereitgestellten Details fest. Die meisten dieser Anforderungen werden vom Clienttreiber in einen Hardware-Lese- oder Schreibvorgang übersetzt. Die Befehle müssen asynchron sein, da das DPM das Warten auf den Abschluss einer Hardwareübertragung nicht blockieren kann.

UcmTcpciCx sendet die Befehle als E/A-Steuerungscode, der den get/set-Vorgang beschreibt, der vom Clienttreiber erforderlich ist. In der Warteschlange des Clienttreibers hat der Treiber seine Warteschlange bei UcmTcpciCx registriert. UcmTcpciCx beginnt mit dem Platzieren von Frameworkanforderungsobjekten in der Warteschlange, für die der Vorgang vom Treiber erforderlich ist. Die E/A-Steuerungscodes werden in der Tabelle in Schritt 4 aufgeführt.

Es liegt in der Verantwortung des Clienttreibers, Anforderungen rechtzeitig auszuführen.

Der Clienttreiber ruft WdfRequestComplete für das Frameworkanforderungsobjekt mit einer Vervollständigung status auf, wenn der angeforderte Vorgang abgeschlossen ist.

Der Clienttreiber muss möglicherweise eine E/A-Anforderung an einen anderen Treiber senden, um den Hardwarevorgang auszuführen. Im Beispiel sendet der Treiber beispielsweise eine SPB-Anforderung an den I2C-verbundenen Portcontroller. In diesem Fall kann der Treiber das Frameworkanforderungsobjekt, das er von UcmTcpciCx empfangen hat, nicht weiterleiten, da das Anforderungsobjekt möglicherweise nicht die richtige Anzahl von Stapelspeicherorten in der WDM-IRP aufweist. Der Clienttreiber muss ein weiteres Frameworkanforderungsobjekt erstellen und an einen anderen Treiber weiterleiten. Der Clienttreiber kann Anforderungsobjekte, die er während der Initialisierung benötigt, vorab zuweisungen, anstatt jedes Mal eins zu erstellen, wenn er eine Anforderung von UcmTcpciCx erhält. Dies ist möglich, da UcmTcpciCx garantiert, dass zu einem bestimmten Zeitpunkt nur eine Anforderung aussteht.

Weitere Informationen