Windows 10 バージョン 1903 以降では、SerCx と SerCx2 には、 GUID_DEVINTERFACE_COMPORT
device インターフェイスの発行のサポートが含まれています。 システム上のアプリケーションとサービスは、このデバイス インターフェイスを使用してシリアル ポートと対話できます。
この機能は、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
は、シリアル ポートのシステム固有の説明 ( UART0
、 UART1
など) で定義されます。
デバイス定義の例 (デバイスを定義するために必要なベンダー固有の情報を除く):
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_COMPORT
device インターフェイス を発行します。 このデバイス インターフェイスには、 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 アプリケーションの場合、デバイス インターフェイスは次のプロセスを使用して配置され、アクセスされます。
- アプリケーション呼び出し
CM_Get_Device_Interface_ListW
を使用して、システム上のすべてのGUID_DEVINTERFACE_COMPORT
クラス デバイス インターフェイスの一覧を取得します - 返されたインターフェイスごとに
CM_Get_Device_Interface_PropertyW
アプリケーション呼び出しを実行し、検出された各インターフェイスのDEVPKEY_DeviceInterface_Serial_PortName
に対してクエリを実行します - 目的のポートが名前で見つかった場合、アプリケーションは (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);
アプリケーションは、デバイスが使用可能になったり使用できなくなったりしたときに、コントローラー/ポートへのハンドルを開いたり閉じたりするために、デバイス インターフェイスの到着とデバイスの削除の通知をサブスクライブ する場合もあります。