Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
À compter de Windows 10 version 1903 et les versions ultérieures, SerCx et SerCx2 incluent la prise en charge de la publication d’une GUID_DEVINTERFACE_COMPORTinterface d’appareil. Les applications et services sur un système peuvent utiliser cette interface d’appareil pour interagir avec le port série.
Cette fonctionnalité peut être activée sur les plateformes soC qui présentent un UART intégré avec un pilote client SerCx/SerCx2, si l’UART est exposé en tant que port physique, ou si des applications régulières (UWP ou Win32) doivent communiquer directement avec un appareil attaché à l’UART. Contrairement à l'accès au contrôleur SerCx/SerCx2 via un ID de connexion - qui permet exclusivement d'accéder à l'UART à partir d'un pilote de périphérique dédié.
Lorsque vous utilisez cette fonctionnalité pour les ports série managés SerCx/SerCx2, un numéro de port COM n’est pas attribué pour ces appareils et aucun lien symbolique n’est créé, ce qui signifie que les applications doivent utiliser l’approche décrite dans ce document pour ouvrir le port série en tant qu’interface d’appareil.
L’utilisation de l’interface de l’appareil (GUID_DEVINTERFACE_COMPORT) est la méthode recommandée pour découvrir et accéder à un port COM. L’utilisation de noms de ports COM hérités est sujette à des collisions de noms et ne fournit pas de notifications de modification d’état à un client. L’utilisation des noms de ports COM hérités n’est pas recommandée et n’est pas prise en charge avec SerCx2 et SerCx.
Activation de la création d'interfaces de périphériques
Vous trouverez ci-dessous les instructions permettant d’activer la création de l’interface d’appareil. Notez que les ports série sont exclusifs, ce qui signifie que si le port série est accessible en tant qu’interface d’appareil, une ressource de connexion dans ACPI ne doit pas être fournie à d’autres appareils , par exemple, aucune ressource ne UARTSerialBusV2 doit être fournie à d’autres appareils sur le système ; le port doit être rendu exclusivement accessible via l’interface de l’appareil.
Configuration ACPI
Un fabricant ou un intégrateur système peut activer ce comportement en modifiant la définition ACPI (ASL) de l’appareil SerCx/SerCx2 existant pour ajouter une _DSD définition pour les propriétés de l’appareil clé-valeur avec UUID daffd814-6eba-4d8c-8a91-bc9bbf4aa301. Dans cette définition, la propriété SerCx-FriendlyName est définie avec une description spécifique du système du port série, par exemple, UART0, UART1etc.
Exemple de définition d’appareil (à l’exclusion des informations spécifiques du fournisseur nécessaires pour définir l’appareil) :
Device(URT0) {
Name(_HID, ...)
Name(_CID, ...)
Name(_DSD, Package() {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package() {
Package(2) {"SerCx-FriendlyName", "UART0"}
}
})
}
L’UUID (daffd814-6eba-4d8c-8a91-bc9bbf4aa301) spécifié doit être utilisé et l’entrée SerCx-FriendlyName doit être définie pour SerCx/SerCx2 pour créer l’interface de l’appareil.
Clé du Registre
À des fins de développement, il SerCxFriendlyName peut également être configuré comme propriété dans la clé matérielle de l’appareil dans le registre. La CM_Open_DevNode_Key méthode peut être utilisée pour accéder à la clé matérielle de l’appareil et ajouter la propriété SerCxFriendlyName à l’appareil, qui est utilisée par SerCx/SerCx2 pour récupérer le nom convivial de l’interface de l’appareil.
Il n’est pas recommandé de définir cette clé par le biais d’un INF d’extension , elle est fournie principalement à des fins de test et de développement. L’approche recommandée consiste à activer la fonctionnalité via ACPI comme indiqué ci-dessus.
Interface de l’appareil
Si un FriendlyName paramètre est défini à l’aide des méthodes ci-dessus, SerCx/SerCx2 publie une GUID_DEVINTERFACE_COMPORTinterface d’appareil pour le contrôleur. Cette interface d’appareil aura la DEVPKEY_DeviceInterface_Serial_PortName propriété définie sur le nom convivial spécifié, qui peut être utilisé par les applications pour localiser un contrôleur/port spécifique.
Activation de l’accès non privilégié
Par défaut, le contrôleur/port est accessible uniquement aux utilisateurs et applications privilégiés. Si l’accès à partir d’applications non privilégiées est requis, le client SerCx/SerCx2 doit remplacer le descripteur de sécurité par défaut après l’appel SerCx2InitializeDeviceInit() ou SerCxDeviceInitConfig(), mais avant d’appeler SerCx2InitializeDevice() ou SerCxInitialize(), au moment où le descripteur de sécurité appliqué est propagé au contrôleur PDO.
Voici un exemple d’activation de l’accès EvtDeviceAdd non privilégié sur SerCx2 à partir du pilote du contrôleur client 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)) {
...
}
...
}
Changements de comportement lors de l’utilisation d’une interface d’appareil
L’adhésion à cette fonctionnalité entraîne les modifications comportementales suivantes dans SerCx/SerCx2 (par opposition à l’accès au contrôleur SerCx/SerCx2 par le biais d’un ID de connexion) :
Aucune configuration par défaut n’est appliquée au port (vitesse, parité, etc.). Comme il n’existe aucune ressource de connexion dans ACPI pour le décrire, le port commence dans un état non initialisé. Les logiciels qui interagissent avec l’interface de l’appareil sont nécessaires pour configurer le port à l’aide de l’interface IOCTL série définie.
Les appels du pilote client SerCx/SerCx2 pour interroger ou appliquer la configuration par défaut retournent un état d’échec. En outre,
IOCTL_SERIAL_APPLY_DEFAULT_CONFIGURATIONles demandes adressées à l’interface de l’appareil échouent, car aucune configuration par défaut n’est spécifiée pour s’appliquer.
Accès à l’interface d’appareil de port série
Pour les applications UWP, l'interface publiée peut être accessible en utilisant les API de l'espace de noms Windows.Devices.SerialCommunication, comme pour n'importe quel autre port série conforme.
Pour les applications Win32, l’interface de l’appareil est située et accessible à l’aide du processus suivant :
- L'application appelle
CM_Get_Device_Interface_ListWpour obtenir une liste de toutes les interfaces de périphérique de classeGUID_DEVINTERFACE_COMPORTsur le système. - L'application appelle
CM_Get_Device_Interface_PropertyWpour chaque interface retournée afin d'interroger leDEVPKEY_DeviceInterface_Serial_PortNamepour chaque interface découverte. - Lorsque le port souhaité est trouvé par nom, l’application utilise la chaîne de liaison symbolique retournée dans (1) pour ouvrir un handle sur le port via
CreateFile()
Exemple de code pour ce flux :
#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);
Notez qu’une application peut également s’abonner aux notifications d’arrivée de l’interface de l’appareil et à la suppression de l’appareil afin d’ouvrir ou de fermer un handle sur le contrôleur/le port lorsque l’appareil devient disponible ou indisponible.