Publicação da interface do dispositivo para uma porta serial gerenciada SerCx ou SerCx2
A partir do Windows 10 versão 1903 e posterior, o SerCx e o SerCx2 incluem suporte para publicar uma interface de GUID_DEVINTERFACE_COMPORT
dispositivo. Aplicativos e serviços em um sistema podem usar essa interface de dispositivo para interagir com a porta serial.
Esse recurso pode ser habilitado em plataformas baseadas em SoC que apresentam um UART integrado com um driver de cliente SerCx/SerCx2, se o UART for exposto como uma porta física ou se aplicativos regulares (UWP ou Win32) precisarem se comunicar diretamente com um dispositivo conectado ao UART. Isso ocorre em oposição ao acesso ao controlador SerCx/SerCx2 por meio de uma ID de conexão, que permite exclusivamente o acesso ao UART a partir de um driver periférico dedicado.
Ao usar esse recurso para portas seriais gerenciadas SerCx/SerCx2, um número de porta COM não é atribuído para esses dispositivos, e nenhum link simbólico é criado. Portanto, os aplicativos devem usar a abordagem descrita neste documento para abrir a porta serial como uma interface de dispositivo.
Usar a interface do dispositivo (GUID_DEVINTERFACE_COMPORT
) é a maneira recomendada de descobrir e acessar uma porta COM. O uso de nomes de porta COM herdados é propenso a colisões de nomes e não fornece notificações de alteração de estado a um cliente. O uso dos nomes de porta COM herdados não é recomendado e não é compatível com SerCx2 e SerCx.
Habilitar a criação da interface do dispositivo
Veja a seguir as instruções para habilitar a criação da interface do dispositivo. As portas seriais são exclusivas, ou seja, se a porta serial for acessível como uma interface de dispositivo, um recurso de conexão na ACPI não deve ser fornecido a nenhum outro dispositivo, por exemplo, nenhum UARTSerialBusV2
recurso deve ser fornecido a nenhum outro dispositivo no sistema; a porta deve ser acessível exclusivamente por meio da interface do dispositivo.
Configuração ACPI
Um fabricante ou integrador de sistema pode habilitar esse comportamento modificando a definição ACPI (ASL) do dispositivo existente SerCx/SerCx2 para adicionar uma _DSD
definição para propriedades de dispositivo de valor-chave com UUID daffd814-6eba-4d8c-8a91-bc9bbf4aa301
. Dentro dessa definição, a propriedade SerCx-FriendlyName
é definida com uma descrição específica do sistema da porta serial, por exemplo, UART0
, UART1
etc.
Exemplo de definição de dispositivo (excluindo informações específicas do fornecedor necessárias para definir o dispositivo):
Device(URT0) {
Name(_HID, ...)
Name(_CID, ...)
Name(_DSD, Package() {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package() {
Package(2) {"SerCx-FriendlyName", "UART0"}
}
})
}
O UUID especificado (daffd814-6eba-4d8c-8a91-bc9bbf4aa301
) deve ser usado, e a entrada SerCx-FriendlyName
deve ser definida para SerCx/SerCx2 para criar a interface do dispositivo.
Chave do Registro
Para fins de desenvolvimento, o SerCxFriendlyName
também pode ser configurado como uma propriedade na chave de hardware do dispositivo no registro. O método CM_Open_DevNode_Key
pode ser usado para acessar a chave de hardware do dispositivo e adicionar a propriedade SerCxFriendlyName
ao dispositivo, que é usada pelo SerCx/SerCx2 para recuperar o nome amigável para a interface do dispositivo.
Não é recomendado definir essa chave por meio de uma extensão INF. Ela é fornecida principalmente para fins de teste e desenvolvimento. A abordagem recomendada é habilitar o recurso via ACPI, conforme documentado acima.
Interface de dispositivo
Se a FriendlyName
for definido usando os métodos acima, SerCx/SerCx2 publicará uma interface de GUID_DEVINTERFACE_COMPORT
dispositivo para o controlador. Essa interface de dispositivo terá a propriedade DEVPKEY_DeviceInterface_Serial_PortName
definida como o nome amigável especificado, que pode ser usado por aplicativos para localizar um controlador/porta específico.
Habilitar o acesso sem privilégios
Por padrão, o controlador/porta estará acessível apenas para usuários e aplicativos privilegiados. Se o acesso de aplicativos sem privilégios for necessário, o cliente SerCx/SerCx2 deverá substituir o descritor de segurança padrão após chamar SerCx2InitializeDeviceInit()
ou SerCxDeviceInitConfig()
, mas antes de chamar SerCx2InitializeDevice()
ou SerCxInitialize()
, momento em que o descritor de segurança aplicado é propagado para o PDO do controlador.
Veja a seguir um exemplo de como habilitar o acesso sem privilégios no SerCx2 de dentro do EvtDeviceAdd
do driver do controlador do cliente 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)) {
...
}
...
}
Alterações de comportamento ao usar uma interface de dispositivo
Aceitar esse recurso resulta nas seguintes alterações comportamentais no SerCx/SerCx2 (em vez de acessar o controlador SerCx/SerCx2 por meio de uma ID de conexão):
Nenhuma configuração padrão é aplicada à porta (velocidade, paridade etc). Como não há nenhum recurso de conexão na ACPI para descrever isso, a porta começa em um estado não inicializado. O software que interage com a interface do dispositivo é necessário para configurar a porta usando a interface IOCTL serial definida.
As chamadas do driver do cliente SerCx/SerCx2 para consultar ou aplicar a configuração padrão retornarão um status de falha. Além disso, as solicitações
IOCTL_SERIAL_APPLY_DEFAULT_CONFIGURATION
para a interface do dispositivo terão falhas, pois não há nenhuma configuração padrão especificada para ser aplicada.
Acessar a interface de dispositivo da porta serial
Para aplicativos UWP, a interface publicada pode ser acessada usando as APIs de namespace Windows.Devices.SerialCommunication
como qualquer outra porta serial compatível.
Para aplicativos Win32, a interface do dispositivo é localizada e acessada usando o seguinte processo:
- O aplicativo chama
CM_Get_Device_Interface_ListW
para obter uma lista de todas as interfaces de dispositivo de classeGUID_DEVINTERFACE_COMPORT
no sistema - O aplicativo chama
CM_Get_Device_Interface_PropertyW
para cada interface retornada para consultar oDEVPKEY_DeviceInterface_Serial_PortName
de cada interface descoberta - Quando a porta desejada é encontrada pelo nome, o aplicativo usa a cadeia de caracteres de link simbólico retornada em (1) para abrir um identificador da porta via
CreateFile()
Exemplo de código para este fluxo:
#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);
Um aplicativo também pode se inscrever para receber notificações de chegada da interface do dispositivo e remoção do dispositivo para abrir ou fechar um identificador do controlador/porta quando o dispositivo se torna disponível ou indisponível.