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.
Un périphérique USB expose ses fonctionnalités sous la forme d’une série d’interfaces appelées configuration USB. Chaque interface se compose d’un ou de plusieurs paramètres alternatifs, et chaque autre paramètre est constitué d’un ensemble de points de terminaison. Cette rubrique décrit les différents descripteurs associés à une configuration USB.
Une configuration USB est décrite dans un descripteur de configuration (voir USB_CONFIGURATION_DESCRIPTOR structure). Un descripteur de configuration contient des informations sur la configuration et ses interfaces, d’autres paramètres et leurs points de terminaison. Chaque descripteur d’interface ou autre paramètre est décrit dans une structure USB_INTERFACE_DESCRIPTOR . Dans une configuration, chaque descripteur d’interface est suivi en mémoire par tous les descripteurs de point de terminaison pour l’interface et un autre paramètre. Chaque descripteur de point de terminaison est stocké dans une structure USB_ENDPOINT_DESCRIPTOR .
Par exemple, considérez un appareil webcam USB décrit dans la disposition des périphériques USB. L’appareil prend en charge une configuration avec deux interfaces, et la première interface (index 0) prend en charge deux autres paramètres.
L’exemple suivant montre le descripteur de configuration pour le périphérique webcam USB :
Configuration Descriptor:
wTotalLength: 0x02CA
bNumInterfaces: 0x02
bConfigurationValue: 0x01
iConfiguration: 0x00
bmAttributes: 0x80 (Bus Powered )
MaxPower: 0xFA (500 mA)
Le champ bConfigurationValue indique le numéro de la configuration définie dans le microprogramme de l’appareil. Le pilote client utilise cette valeur numérique pour sélectionner une configuration active. Pour plus d’informations sur la configuration d’un périphérique USB, consultez Comment sélectionner une configuration pour un périphérique USB. Une configuration USB indique également certaines caractéristiques de puissance. Le bmAttributes contient un masque de bits qui indique si la configuration prend en charge la fonctionnalité de mise en éveil à distance et si l’appareil est alimenté par bus ou auto-alimenté. Le champ MaxPower spécifie la puissance maximale (en unités milliamp) que l’appareil peut tirer de l’hôte, lorsque l’appareil est alimenté par bus. Le descripteur de configuration indique également le nombre total d’interfaces (bNumInterfaces) prises en charge par l’appareil.
L’exemple suivant montre le descripteur d’interface pour l’autre paramètre 0 de l’interface 0 pour l’appareil webcam :
Interface Descriptor:
bInterfaceNumber: 0x00
bAlternateSetting: 0x00
bNumEndpoints: 0x01
bInterfaceClass: 0x0E
bInterfaceSubClass: 0x02
bInterfaceProtocol: 0x00
iInterface: 0x02
0x0409: "Microsoft LifeCam VX-5000"
0x0409: "Microsoft LifeCam VX-5000"
Dans l’exemple précédent, notez les valeurs de champ bInterfaceNumber et bAlternateSetting . Ces champs contiennent des valeurs d’index qu’un pilote client utilise pour activer l’interface et l’un de ses autres paramètres. Pour l’activation, le pilote envoie une demande de sélection d'interface à la pile de pilotes USB. La pile de gestionnaires de périphériques génère ensuite une demande de contrôle standard (SET INTERFACE) et l’envoie au périphérique. Notez le champ bInterfaceClass . Le descripteur d’interface ou le descripteur pour l’un de ses autres paramètres spécifie un code de classe, une sous-classe et un protocole. La valeur de 0x0E indique que l’interface correspond à la classe d’appareil vidéo. Notez également le champ iInterface . Cette valeur indique qu’il existe deux descripteurs de chaîne ajoutés au descripteur d’interface. Les descripteurs de chaîne contiennent des descriptions Unicode utilisées pendant l’énumération de l’appareil pour identifier les fonctionnalités. Pour plus d’informations sur les descripteurs de chaîne, consultez les descripteurs de chaîne USB.
Chaque point de terminaison, dans une interface, décrit un seul flux d’entrée ou de sortie pour l’appareil. Un appareil qui prend en charge les flux pour différents types de fonctions a plusieurs interfaces. Un appareil qui prend en charge plusieurs flux relatifs à une fonction peut prendre en charge plusieurs points de terminaison sur une seule interface.
Tous les types de points de terminaison (à l’exception du point de terminaison par défaut) doivent fournir des descripteurs de point de terminaison afin que l’hôte puisse obtenir des informations sur le point de terminaison. Un descripteur de point de terminaison inclut des informations, telles que son adresse, son type, sa direction et la quantité de données que le point de terminaison peut gérer. Les transferts de données vers le point de terminaison sont basés sur ces informations.
L’exemple suivant montre un descripteur de point de terminaison pour l’appareil webcam :
Endpoint Descriptor:
bEndpointAddress: 0x82 IN
bmAttributes: 0x01
wMaxPacketSize: 0x0080 (128)
bInterval: 0x01
Le champ bEndpointAddress spécifie l’adresse de point de terminaison unique qui contient le numéro de point de terminaison (Bits 3..0) et la direction du point de terminaison (Bit 7). En lisant ces valeurs dans l’exemple précédent, nous pouvons déterminer que le descripteur décrit un point de terminaison IN dont le numéro de point de terminaison est 2. L’attribut bmAttributes indique que le type de point de terminaison est isochronous. Le champ wMaxPacketSizefield indique le nombre maximal d’octets que le point de terminaison peut envoyer ou recevoir dans une seule transaction. Les bits 12..11 indiquent le nombre total de transactions qui peuvent être envoyées par microframe. BInterval indique la fréquence à laquelle le point de terminaison peut envoyer ou recevoir des données.
Comment obtenir le descripteur de configuration
Le descripteur de configuration est obtenu à partir de l’appareil via une demande d’appareil standard (GET_DESCRIPTOR), qui est envoyée en tant que transfert de contrôle par la pile de pilotes USB. Un pilote client USB peut lancer la requête de l’une des manières suivantes :
Si l’appareil ne prend en charge qu’une seule configuration, le moyen le plus simple consiste à appeler la méthode WdfUsbTargetDeviceRetrieveConfigDescriptor fournie par l’infrastructure.
Pour un appareil qui prend en charge plusieurs configurations, si le pilote client souhaite obtenir le descripteur de la configuration autre que le premier, le pilote doit envoyer un URB. Pour envoyer un URB, le pilote doit allouer, mettre en forme, puis soumettre l’URB à la pile de pilotes USB.
Pour allouer l’URB, le pilote client doit appeler la méthode WdfUsbTargetDeviceCreateUrb . La méthode reçoit un pointeur vers une URB allouée par la pile de pilotes USB.
Pour mettre en forme l’URB, le pilote client peut utiliser la macro UsbBuildGetDescriptorRequest . La macro définit toutes les informations nécessaires dans l’URB, telles que le numéro de configuration défini par l’appareil pour lequel récupérer le descripteur. La fonction URB est définie sur URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE (voir _URB_CONTROL_DESCRIPTOR_REQUEST) et le type de descripteur est défini sur USB_CONFIGURATION_DESCRIPTOR_TYPE. En utilisant les informations contenues dans l’URB, la pile de pilotes USB génère une demande de contrôle standard et l’envoie à l’appareil.
Pour envoyer l’URB, le pilote client doit utiliser un objet de requête WDF. Pour envoyer l’objet de requête à la pile de pilotes USB de manière asynchrone, le pilote doit appeler la méthode **WdfRequestSend**. Pour l’envoyer de façon synchrone, appelez la méthode WdfUsbTargetDeviceSendUrbSynchronously .
Pilotes WDM : Un pilote client WDM (Windows Driver Model) ne peut obtenir le descripteur de configuration qu’en envoyant un URB. Pour allouer l’URB, le pilote doit appeler la routine USBD_UrbAllocate . Pour mettre en forme l’URB, le pilote doit appeler la macro UsbBuildGetDescriptorRequest . Pour soumettre l’URB, le pilote doit associer l’URB à un IRP et soumettre l’IRP à la pile de pilotes USB. Pour plus d’informations, consultez Comment envoyer un URB.
Dans une configuration USB, le nombre d’interfaces et leurs autres paramètres sont variables. Par conséquent, il est difficile de prédire la taille de la mémoire tampon requise pour contenir le descripteur de configuration. Le pilote client doit collecter toutes ces informations en deux étapes. Tout d’abord, déterminez la mémoire tampon de taille requise pour contenir tout le descripteur de configuration, puis émettez une demande pour récupérer l’intégralité du descripteur. Un pilote client peut obtenir la taille de l’une des manières suivantes :
Pour obtenir le descripteur de configuration en appelant WdfUsbTargetDeviceRetrieveConfigDescriptor, procédez comme suit :
- Obtenez la taille de la mémoire tampon requise pour contenir toutes les informations de configuration en appelant WdfUsbTargetDeviceRetrieveConfigDescriptor. Le pilote doit passer NULL dans la mémoire tampon et une variable pour contenir la taille de la mémoire tampon.
- Allouez une mémoire tampon plus grande en fonction de la taille reçue via l’appel WdfUsbTargetDeviceRetrieveConfigDescriptor précédent.
- Appelez À nouveau WdfUsbTargetDeviceRetrieveConfigDescriptor et spécifiez un pointeur vers la nouvelle mémoire tampon allouée à l’étape 2.
NTSTATUS RetrieveDefaultConfigurationDescriptor (
_In_ WDFUSBDEVICE UsbDevice,
_Out_ PUSB_CONFIGURATION_DESCRIPTOR *ConfigDescriptor
)
{
NTSTATUS ntStatus = -1;
USHORT sizeConfigDesc;
PUSB_CONFIGURATION_DESCRIPTOR fullConfigDesc = NULL;
PAGED_CODE();
*ConfigDescriptor = NULL;
ntStatus = WdfUsbTargetDeviceRetrieveConfigDescriptor (
UsbDevice,
NULL,
&sizeConfigDesc);
if (sizeConfigDesc == 0)
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"%!FUNC! Could not retrieve the configuration descriptor size.");
goto Exit;
}
else
{
fullConfigDesc = (PUSB_CONFIGURATION_DESCRIPTOR) ExAllocatePoolWithTag (
NonPagedPool,
sizeConfigDesc,
USBCLIENT_TAG);
if (!fullConfigDesc)
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
}
RtlZeroMemory (fullConfigDesc, sizeConfigDesc);
ntStatus = WdfUsbTargetDeviceRetrieveConfigDescriptor (
UsbDevice,
fullConfigDesc,
&sizeConfigDesc);
if (!NT_SUCCESS(ntStatus))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"%!FUNC! Could not retrieve the configuration descriptor.");
goto Exit;
}
*ConfigDescriptor = fullConfigDesc;
Exit:
return ntStatus;
}
Pour obtenir le descripteur de configuration en envoyant un URB, procédez comme suit :
- Allouez un URB en appelant la méthode WdfUsbTargetDeviceCreateUrb .
- Mettez en forme l’URB en appelant la macro UsbBuildGetDescriptorRequest . La mémoire tampon de transfert de l’URB doit pointer vers une mémoire tampon suffisamment grande pour contenir une structure USB_CONFIGURATION_DESCRIPTOR .
- Envoyez l'URB en tant qu'objet de requête WDF en appelant WdfRequestSend ou WdfUsbTargetDeviceSendUrbSynchronously.
- Une fois la requête terminée, vérifiez le membre wTotalLength de USB_CONFIGURATION_DESCRIPTOR. Cette valeur indique la taille de la mémoire tampon requise pour contenir un descripteur de configuration complet.
- Allouez une mémoire tampon plus grande en fonction de la taille récupérée dans wTotalLength.
- Émettez la même requête avec la mémoire tampon plus grande.
L’exemple de code suivant montre l’appel UsbBuildGetDescriptorRequest pour obtenir des informations de configuration pour la configuration i-th :
NTSTATUS FX3_RetrieveConfigurationDescriptor (
_In_ WDFUSBDEVICE UsbDevice,
_In_ PUCHAR ConfigurationIndex,
_Out_ PUSB_CONFIGURATION_DESCRIPTOR *ConfigDescriptor
)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
USB_CONFIGURATION_DESCRIPTOR configDesc;
PUSB_CONFIGURATION_DESCRIPTOR fullConfigDesc = NULL;
PURB urb = NULL;
WDFMEMORY urbMemory = NULL;
PAGED_CODE();
RtlZeroMemory (&configDesc, sizeof(USB_CONFIGURATION_DESCRIPTOR));
*ConfigDescriptor = NULL;
// Allocate an URB for the get-descriptor request.
// WdfUsbTargetDeviceCreateUrb returns the address of the
// newly allocated URB and the WDFMemory object that
// contains the URB.
ntStatus = WdfUsbTargetDeviceCreateUrb (
UsbDevice,
NULL,
&urbMemory,
&urb);
if (!NT_SUCCESS (ntStatus))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"%!FUNC! Could not allocate URB for an open-streams request.");
goto Exit;
}
// Format the URB.
UsbBuildGetDescriptorRequest (
urb, // Points to the URB to be formatted
(USHORT) sizeof( struct _URB_CONTROL_DESCRIPTOR_REQUEST ), // Size of the URB.
USB_CONFIGURATION_DESCRIPTOR_TYPE, // Type of descriptor
*ConfigurationIndex, // Index of the configuration
0, // Not used for configuration descriptors
&configDesc, // Points to a USB_CONFIGURATION_DESCRIPTOR structure
NULL, // Not required because we are providing a buffer not MDL
sizeof(USB_CONFIGURATION_DESCRIPTOR), // Size of the USB_CONFIGURATION_DESCRIPTOR structure.
NULL // Reserved.
);
// Send the request synchronously.
ntStatus = WdfUsbTargetDeviceSendUrbSynchronously (
UsbDevice,
NULL,
NULL,
urb);
if (configDesc.wTotalLength == 0)
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"%!FUNC! Could not retrieve the configuration descriptor size.");
ntStatus = USBD_STATUS_INAVLID_CONFIGURATION_DESCRIPTOR;
goto Exit;
}
// Allocate memory based on the retrieved size.
// The allocated memory is released by the caller.
fullConfigDesc = (PUSB_CONFIGURATION_DESCRIPTOR) ExAllocatePoolWithTag (
NonPagedPool,
configDesc.wTotalLength,
USBCLIENT_TAG);
RtlZeroMemory (fullConfigDesc, configDesc.wTotalLength);
if (!fullConfigDesc)
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
// Format the URB.
UsbBuildGetDescriptorRequest (
urb,
(USHORT) sizeof( struct _URB_CONTROL_DESCRIPTOR_REQUEST ),
USB_CONFIGURATION_DESCRIPTOR_TYPE,
*ConfigurationIndex,
0,
fullConfigDesc,
NULL,
configDesc.wTotalLength,
NULL
);
// Send the request again.
ntStatus = WdfUsbTargetDeviceSendUrbSynchronously (
UsbDevice,
NULL,
NULL,
urb);
if ((fullConfigDesc->wTotalLength == 0) || !NT_SUCCESS (ntStatus))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"%!FUNC! Could not retrieve the configuration descriptor.");
ntStatus = USBD_STATUS_INAVLID_CONFIGURATION_DESCRIPTOR;
goto Exit;
}
// Return to the caller.
*ConfigDescriptor = fullConfigDesc;
Exit:
if (urbMemory)
{
WdfObjectDelete (urbMemory);
}
return ntStatus;
}
Lorsque l’appareil retourne le descripteur de configuration, la mémoire tampon de requête est remplie avec des descripteurs d’interface pour tous les autres paramètres et des descripteurs de point de terminaison pour tous les points de terminaison au sein d’un autre paramètre particulier. Pour l’appareil décrit dans la disposition du périphérique USB, le diagramme suivant illustre la façon dont les informations de configuration sont disposées en mémoire.
Le membre bInterfaceNumber de base zéro de USB_INTERFACE_DESCRIPTOR distingue les interfaces au sein d’une configuration. Pour une interface donnée, le membre bAlternateSetting de base zéro fait la distinction entre d’autres paramètres de l’interface. L’appareil retourne des descripteurs d’interface dans l’ordre des valeurs bInterfaceNumber , puis dans l’ordre des valeurs bAlternateSetting .
Pour rechercher un descripteur d’interface donné dans la configuration, le pilote client peut appeler USBD_ParseConfigurationDescriptorEx. Dans l’appel, le pilote client fournit une position de départ dans la configuration. Si vous le souhaitez, le pilote peut spécifier un numéro d’interface, un autre paramètre, une classe, une sous-classe ou un protocole. La routine retourne un pointeur vers le descripteur d’interface correspondant suivant.
Pour examiner un descripteur de configuration pour un descripteur de point de terminaison ou de chaîne, utilisez la routine USBD_ParseDescriptors . L’appelant fournit une position de départ dans la configuration et un type de descripteur, tel que USB_STRING_DESCRIPTOR_TYPE ou USB_ENDPOINT_DESCRIPTOR_TYPE. La routine retourne un pointeur vers le descripteur correspondant suivant.