次の方法で共有


SerCx または SerCx2 マネージド シリアル ポートのデバイス インターフェイス の公開

Windows 10 バージョン 1903 以降では、SerCx と SerCx2 には、 GUID_DEVINTERFACE_COMPORTdevice インターフェイスの発行のサポートが含まれています。 システム上のアプリケーションとサービスは、このデバイス インターフェイスを使用してシリアル ポートと対話できます。

この機能は、UART が物理ポートとして公開されている場合、または通常のアプリケーション (UWP または Win32) が UART に接続されているデバイスと直接通信する必要がある場合に、SerCx/SerCx2 クライアント ドライバーと統合された UART を備えた SoC ベースのプラットフォームで有効にすることができます。 これは、専用の周辺機器ドライバーから UART へのアクセスを排他的に有効にする 接続 ID を介して SerCx/SerCx2 コントローラー にアクセスするのとは対照的です。

SerCx/SerCx2 マネージド シリアル ポートにこの機能を使用する場合、これらのデバイスには COM ポート番号が割り当てられず、シンボリック リンクは作成されません。つまり、アプリケーションは、このドキュメントで説明されている方法を使用してデバイス インターフェイスとしてシリアル ポートを開く必要があります。

COM ポートを検出してアクセスするには、デバイス インターフェイス (GUID_DEVINTERFACE_COMPORT) を使用することをお勧めします。 従来の COM ポート名を使用すると、名前の競合が発生しやすく、クライアントに状態変更通知は提供されません。 従来の COM ポート名の使用はお勧めしません。SerCx2 および SerCx ではサポートされていません。

デバイス インターフェイスの作成の有効化

デバイス インターフェイスの作成を有効にする手順を次に示します。 シリアル ポートは 排他的です。つまり、シリアル ポートにデバイス インターフェイスとしてアクセスできる場合は、ACPI 内の接続リソースを他のデバイスに提供 しないでください 。たとえば、システム上の他のデバイスに UARTSerialBusV2 リソースを提供する必要はありません。ポートはデバイス インターフェイスを介して 排他的に アクセスできるようにする必要があります。

ACPI の構成

システムの製造元またはインテグレーターは、既存の SerCx/SerCx2 デバイスの ACPI (ASL) 定義を変更して、UUID daffd814-6eba-4d8c-8a91-bc9bbf4aa301を持つキー値デバイス プロパティの_DSD定義を追加することで、この動作を有効にすることができます。 この定義内では、プロパティ SerCx-FriendlyName は、シリアル ポートのシステム固有の説明 ( UART0UART1 など) で定義されます。

デバイス定義の例 (デバイスを定義するために必要なベンダー固有の情報を除く):

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

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

指定した UUID (daffd814-6eba-4d8c-8a91-bc9bbf4aa301) を使用し、SerCx/SerCx2 SerCx-FriendlyNameエントリを定義してデバイス インターフェイスを作成する必要があります。

レジストリ キー

開発の目的で、 SerCxFriendlyName をレジストリ内のデバイスのハードウェア キーのプロパティとして構成することもできます。 CM_Open_DevNode_Key メソッドを使用して、デバイスのハードウェア キーにアクセスし、プロパティ SerCxFriendlyNameをデバイスに追加できます。このプロパティは、SerCx/SerCx2 によってデバイス インターフェイスのフレンドリ名を取得するために使用されます。

拡張 INF を使用してこのキーを設定することはお勧めしません。主にテストと開発の目的で提供されます。 前述のように、ACPI を使用して機能を有効にすることをお勧めします。

デバイス インターフェイス

上記のメソッドを使用して FriendlyName が定義されている場合、SerCx/SerCx2 はコントローラーの GUID_DEVINTERFACE_COMPORTdevice インターフェイス を発行します。 このデバイス インターフェイスには、 DEVPKEY_DeviceInterface_Serial_PortName プロパティが指定されたフレンドリ名に設定されます。これは、アプリケーションが特定のコントローラー/ポートを見つけるために使用できます。

特権のないアクセスの有効化

既定では、コントローラー/ポートには特権ユーザーとアプリケーションのみがアクセスできます。 特権のないアプリケーションからのアクセスが必要な場合、SerCx/SerCx2 クライアントは、 SerCx2InitializeDeviceInit() または SerCxDeviceInitConfig()を呼び出した後、 SerCx2InitializeDevice() または SerCxInitialize()を呼び出す前に、既定のセキュリティ記述子をオーバーライドする必要があります。その時点で、適用されたセキュリティ記述子がコントローラー PDO に伝達されます。

SerCx2 クライアント コントローラー ドライバーの EvtDeviceAdd 内から SerCx2 で特権のないアクセスを有効にする方法の例を次に示します。

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)) {
        ...
    }

    ...
}

デバイス インターフェイスを使用する場合の動作の変更

この機能をオプトインすると、SerCx/SerCx2 で次の動作が変更されます ( 接続 ID を介して SerCx/SerCx2 コントローラーにアクセスするのとは対照的です)。

  • 既定の構成はポートに適用されません (速度、パリティなど)。 ACPI にこれを説明する接続リソースがないため、ポートは初期化されていない状態で開始されます。 定義された シリアル IOCTL インターフェイスを使用してポートを構成するには、デバイス インターフェイスと対話するソフトウェアが必要です。

  • SerCx/SerCx2 クライアント ドライバーからクエリを実行するか、既定の構成を適用する呼び出しは、エラー状態を返します。 さらに、適用する既定の構成が指定されていないため、デバイス インターフェイスへの IOCTL_SERIAL_APPLY_DEFAULT_CONFIGURATION 要求は失敗します。

シリアル ポート デバイス インターフェイスへのアクセス

UWP アプリケーションの場合、他の準拠しているシリアル ポートと同様に、 Windows.Devices.SerialCommunication 名前空間 API を使用して、発行されたインターフェイスにアクセスできます。

Win32 アプリケーションの場合、デバイス インターフェイスは次のプロセスを使用して配置され、アクセスされます。

  1. アプリケーション呼び出し CM_Get_Device_Interface_ListW を使用して、システム上のすべての GUID_DEVINTERFACE_COMPORT クラス デバイス インターフェイスの一覧を取得します
  2. 返されたインターフェイスごとに CM_Get_Device_Interface_PropertyW アプリケーション呼び出しを実行し、検出された各インターフェイスの DEVPKEY_DeviceInterface_Serial_PortName に対してクエリを実行します
  3. 目的のポートが名前で見つかった場合、アプリケーションは (1) で返されたシンボリック リンク文字列を使用して、ポートへのハンドルを開きます。 CreateFile()

このフローのサンプル コード:

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

アプリケーションは、デバイスが使用可能になったり使用できなくなったりしたときに、コントローラー/ポートへのハンドルを開いたり閉じたりするために、デバイス インターフェイスの到着とデバイスの削除の通知をサブスクライブ する場合もあります。