Freigeben über


Geräteschnittstellenveröffentlichung für einen von SerCx oder SerCx2 verwalteten seriellen Port

Ab Windows 10, Version 1903 und höher, unterstützen SerCx und SerCx2 die Veröffentlichung einer GUID_DEVINTERFACE_COMPORTGeräteschnittstelle. Anwendungen und Dienste auf einem System können diese Geräteschnittstelle verwenden, um mit dem seriellen Port zu interagieren.

Dieses Feature kann auf SoC-basierten Plattformen aktiviert werden, die eine integrierte UART mit einem SerCx/SerCx2-Clienttreiber aufweisen, wenn die UART als physischer Port verfügbar gemacht wird oder reguläre Anwendungen (UWP oder Win32) direkt mit einem gerät kommunizieren müssen, das an die UART angeschlossen ist. Dies ist im Gegensatz zum Zugriff auf den SerCx/SerCx2-Controller über eine Verbindungs-ID , die ausschließlich den Zugriff auf die UART von einem dedizierten Peripherietreiber ermöglicht.

Wenn Sie dieses Feature für verwaltete serielle Ports serCx/SerCx2 verwenden, wird keine COM-Portnummer für diese Geräte zugewiesen, und es wird keine symbolische Verknüpfung erstellt . Dies bedeutet, dass Anwendungen den in diesem Dokument beschriebenen Ansatz verwenden müssen, um den seriellen Port als Geräteschnittstelle zu öffnen.

Die Verwendung der Geräteschnittstelle (GUID_DEVINTERFACE_COMPORT) ist die empfohlene Methode zum Ermitteln und Zugreifen auf einen COM-Port. Die Verwendung von Legacy-COM-Portnamen ist anfällig für Namenskonflikte und stellt keine Statusänderungsbenachrichtigungen für einen Client bereit. Die Verwendung der älteren COM-Portnamen wird nicht empfohlen und nicht mit SerCx2 und SerCx unterstützt.

Aktivieren der Geräteschnittstellenerstellung

Im Folgenden finden Sie die Anweisungen zum Aktivieren der Geräteschnittstellenerstellung. Beachten Sie, dass serielle Ports exklusiv sind, d. h., wenn auf den seriellen Anschluss als Geräteschnittstelle zugegriffen werden kann, sollte eine Verbindungsressource in ACPI nicht für andere Geräte bereitgestellt werden , z. B. keine UARTSerialBusV2 Ressource sollte für andere Geräte im System bereitgestellt werden; der Port sollte ausschließlich über die Geräteschnittstelle zugänglich gemacht werden.

ACPI-Konfiguration

Ein Systemhersteller oder Integrator kann dieses Verhalten aktivieren, indem die ACPI-Definition (ASL) des vorhandenen SerCx/SerCx2-Geräts geändert wird, um eine _DSD Definition für Schlüsselwertgeräteeigenschaften mit UUID daffd814-6eba-4d8c-8a91-bc9bbf4aa301hinzuzufügen. Innerhalb dieser Definition wird die Eigenschaft SerCx-FriendlyName mit einer systemspezifischen Beschreibung des seriellen Ports definiert, UART0z. B. , , UART1usw.

Beispiel für die Gerätedefinition (mit Ausnahme von herstellerspezifischen Informationen, die zum Definieren des Geräts erforderlich sind):

    Device(URT0) {
        Name(_HID, ...)
        Name(_CID, ...)

        Name(_DSD, Package() {
            ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
            Package() {
                Package(2) {"SerCx-FriendlyName", "UART0"}
            }
        })
    }

Die angegebene UUID (daffd814-6eba-4d8c-8a91-bc9bbf4aa301) muss verwendet werden, und der Eintrag SerCx-FriendlyName muss für SerCx/SerCx2 definiert werden, um die Geräteschnittstelle zu erstellen.

Registrierungsschlüssel

Zu Entwicklungszwecken kann dies SerCxFriendlyName auch als Eigenschaft im Hardwareschlüssel des Geräts in der Registrierung konfiguriert werden. Die CM_Open_DevNode_Key-Methode kann verwendet werden, um auf den Hardwareschlüssel des Geräts zuzugreifen und dem Gerät die Eigenschaft SerCxFriendlyName hinzuzufügen, die SerCx/SerCx2 nutzt, um den Anzeigenamen der Geräteschnittstelle abzurufen.

Es wird nicht empfohlen, diesen Schlüssel über eine Erweiterung INF festzulegen – es wird hauptsächlich für Test- und Entwicklungszwecke bereitgestellt. Der empfohlene Ansatz besteht darin, das Feature über ACPI wie oben dokumentiert zu aktivieren.

Geräteschnittstelle

Wenn eine FriendlyName mithilfe der oben genannten Methoden definiert ist, veröffentlicht SerCx/SerCx2 eine GUID_DEVINTERFACE_COMPORTGeräteschnittstelle für den Controller. Diese Geräteschnittstelle hat die DEVPKEY_DeviceInterface_Serial_PortName Eigenschaft auf den angegebenen freundlichen Namen festgelegt, der von Anwendungen verwendet werden kann, um einen bestimmten Controller/Port zu finden.

Aktivieren des nicht privilegierten Zugriffs

Standardmäßig kann der Controller/Port nur für privilegierte Benutzer und Anwendungen zugänglich sein. Wenn der Zugriff von nicht privilegierten Anwendungen erforderlich ist, muss der SerCx/SerCx2-Client den Standardsicherheitsdeskriptor nach dem Aufruf SerCx2InitializeDeviceInit() oder SerCxDeviceInitConfig(), aber vor dem Aufruf SerCx2InitializeDevice() oder SerCxInitialize(), zu dem Zeitpunkt, zu dem der angewendete Sicherheitsdeskriptor an den Controller-PDO weitergegeben wird, überschreiben.

Ein Beispiel für das Aktivieren des nicht privilegierten Zugriffs auf SerCx2 innerhalb des SerCx2-Clientcontrollertreibers EvtDeviceAdd finden Sie unten.

SampleControllerEvtDeviceAdd(
    WDFDRIVER WdfDriver,
    WDFDEVICE_INIT WdfDeviceInit
)
{
    ...

    NTSTATUS status = SerCx2InitializeDeviceInit(WdfDeviceInit);
    if (!NT_SUCCESS(status)) {
        ...
    }

    // Declare a security descriptor allowing access to all
    DECLARE_CONST_UNICODE_STRING(
        SDDL_DEVOBJ_SERCX_SYS_ALL_ADM_ALL_UMDF_ALL_USERS_RDWR,
        L"D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;UD)(A;;GRGW;;;BU)");

    // Assign it to the device, overwriting the default SerCx2 security descriptor
    status = WdfDeviceInitAssignSDDLString(
                WdfDeviceInit,
                &SDDL_DEVOBJ_SERCX_SYS_ALL_ADM_ALL_UMDF_ALL_USERS_RDWR);

    if (!NT_SUCCESS(status)) {
        ...
    }

    ...
}

Verhaltensänderungen bei Verwendung einer Geräteschnittstelle

Die Anmeldung zu diesem Feature führt zu den folgenden Verhaltensänderungen in SerCx/SerCx2 (im Gegensatz zum Zugriff auf den SerCx/SerCx2-Controller über eine Verbindungs-ID):

  • Es wird keine Standardkonfiguration auf den Port angewendet (Geschwindigkeit, Parität usw.). Da in ACPI keine Verbindungsressource zur Beschreibung vorhanden ist, beginnt der Port in einem nicht initialisierten Zustand. Software, die mit der Geräteschnittstelle interagiert, ist erforderlich, um den Port mithilfe der definierten seriellen IOCTL-Schnittstelle zu konfigurieren.

  • Aufrufe vom SerCx/SerCx2-Clienttreiber, um die Standardkonfiguration abzufragen oder anzuwenden, gibt einen Fehlerstatus zurück. Darüber hinaus sind Anforderungen an die Geräteschnittstelle fehlgeschlagen, IOCTL_SERIAL_APPLY_DEFAULT_CONFIGURATION da keine Standardkonfiguration angegeben ist, die angewendet werden soll.

Zugreifen auf die Schnittstelle des seriellen Portgeräts

Für UWP-Anwendungen kann auf die veröffentlichte Schnittstelle mithilfe der Windows.Devices.SerialCommunication Namespace-APIs wie jeder andere kompatible serielle Port zugegriffen werden.

Für Win32-Anwendungen wird die Geräteschnittstelle mithilfe des folgenden Prozesses gefunden und darauf zugegriffen:

  1. Anwendung ruft CM_Get_Device_Interface_ListW auf, um eine Liste von allen GUID_DEVINTERFACE_COMPORT-Klassengeräteschnittstellen auf dem System abzurufen.
  2. Anwendungsaufrufe CM_Get_Device_Interface_PropertyW für jede zurückgegebene Schnittstelle, um die DEVPKEY_DeviceInterface_Serial_PortName für jede ermittelte Schnittstelle abzufragen
  3. Wenn der gewünschte Port anhand des Namens gefunden wird, verwendet die Anwendung die in (1) zurückgegebene symbolische Verknüpfungszeichenfolge, um einen Handle für den Port zu öffnen. CreateFile()

Beispielcode für diesen Fluss:

#include <windows.h>
#include <cfgmgr32.h>
#include <initguid.h>
#include <devpropdef.h>
#include <devpkey.h>
#include <ntddser.h>

...

DWORD ret;
ULONG deviceInterfaceListBufferLength;

//
// Determine the size (in characters) of buffer required for to fetch a list of
// all GUID_DEVINTERFACE_COMPORT device interfaces present on the system.
//
ret = CM_Get_Device_Interface_List_SizeW(
        &deviceInterfaceListBufferLength,
        (LPGUID) &GUID_DEVINTERFACE_COMPORT,
        NULL,
        CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (ret != CR_SUCCESS) {
    // Handle error
    ...
}

//
// Allocate buffer of the determined size.
//
PWCHAR deviceInterfaceListBuffer = (PWCHAR) malloc(deviceInterfaceListBufferLength * sizeof(WCHAR));
if (deviceInterfaceListBuffer == NULL) {
    // Handle error
    ...
}

//
// Fetch the list of all GUID_DEVINTERFACE_COMPORT device interfaces present
// on the system.
//
ret = CM_Get_Device_Interface_ListW(
        (LPGUID) &GUID_DEVINTERFACE_COMPORT,
        NULL,
        deviceInterfaceListBuffer,
        deviceInterfaceListBufferLength,
        CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (ret != CR_SUCCESS) {
    // Handle error
    ...
}

//
// Iterate through the list, examining one interface at a time
//
PWCHAR currentInterface = deviceInterfaceListBuffer;
while (*currentInterface) {
    //
    // Fetch the DEVPKEY_DeviceInterface_Serial_PortName for this interface
    //
    CONFIGRET configRet;
    DEVPROPTYPE devPropType;
    PWCHAR devPropBuffer;
    ULONG devPropSize = 0;

    // First, get the size of buffer required
    configRet = CM_Get_Device_Interface_PropertyW(
        currentInterface,
        &DEVPKEY_DeviceInterface_Serial_PortName,
        &devPropType,
        NULL,
        &devPropSize,
        0);
    if (configRet != CR_BUFFER_SMALL) {
        // Handle error
        ...
    }

    // Allocate the buffer
    devPropBuffer = malloc(devPropSize);
    if (devPropBuffer == NULL) {
        // Handle error
        free(devPropBuffer);
        ...
    }

    configRet = CM_Get_Device_Interface_PropertyW(
        currentInterface,
        &DEVPKEY_DeviceInterface_Serial_PortName,
        &devPropType,
        (PBYTE) devPropBuffer,
        &devPropSize,
        0);
    if (configRet != CR_SUCCESS) {
        // Handle error
        free(devPropBuffer);
        ...
    }

    // Verify the value is the correct type and size
    if ((devPropType != DEVPROP_TYPE_STRING) ||
        (devPropSize < sizeof(WCHAR))) {
        // Handle error
        free(devPropBuffer);
        ...
    }

    // Now, check if the interface is the one we are interested in
    if (wcscmp(devPropBuffer, L"UART0") == 0) {
        free(devPropBuffer);
        break;
    }

    // Advance to the next string (past the terminating NULL)
    currentInterface += wcslen(currentInterface) + 1;
    free(devPropBuffer);
}

//
// currentInterface now either points to NULL (there was no match and we iterated
// over all interfaces without a match) - or, it points to the interface with
// the friendly name UART0, in which case we can open it.
//
if (*currentInterface == L'\0') {
    // Handle interface not found error
    ...
}

//
// Now open the device interface as we would a COMx style serial port.
//
HANDLE portHandle = CreateFileW(
                        currentInterface,
                        GENERIC_READ | GENERIC_WRITE,
                        0,
                        NULL,
                        OPEN_EXISTING,
                        0,
                        NULL);
if (portHandle == INVALID_HANDLE_VALUE) {
    // Handle error
    ...
}

free(deviceInterfaceListBuffer);
deviceInterfaceListBuffer = NULL;
currentInterface = NULL;

//
// We are now able to send IO requests to the device.
//
... = ReadFile(portHandle, ..., ..., ..., NULL);

Beachten Sie, dass eine Anwendung auch Benachrichtigungen über das Eintreffen und Entfernen der Geräteschnittstelle empfangen kann, um eine Zugriffssteuerung für den Controller/Port zu öffnen oder zu schließen, wenn das Gerät verfügbar oder nicht mehr verfügbar ist.