Partager via


Comment envoyer un transfert de contrôle USB

Cet article explique la structure d’un transfert de contrôle et la façon dont un pilote client doit envoyer une demande de contrôle à l’appareil.

À propos du point de terminaison par défaut

Tous les périphériques USB doivent prendre en charge au moins un point de terminaison appelé point de terminaison par défaut. Tout transfert qui cible le point de terminaison par défaut est appelé transfert de contrôle. L’objectif d’un transfert de contrôle est de permettre à l’hôte d’obtenir des informations sur l’appareil, de configurer l’appareil ou d’effectuer des opérations de contrôle propres à l’appareil.

Commençons par étudier ces caractéristiques du point de terminaison par défaut.

  • L’adresse du point de terminaison par défaut est 0.
  • Le point de terminaison par défaut est bidirectionnel, c’est-à-dire que l’hôte peut envoyer des données au point de terminaison et recevoir des données de celui-ci dans un seul transfert.
  • Le point de terminaison par défaut est disponible au niveau de l’appareil et n’est défini dans aucune interface de l’appareil.
  • Le point de terminaison par défaut est actif dès qu’une connexion est établie entre l’hôte et l’appareil. Il est actif avant même qu’une configuration ne soit sélectionnée.
  • La taille maximale du paquet du point de terminaison par défaut dépend de la vitesse de bus de l’appareil. Vitesse faible, 8 octets ; pleine et haute vitesse, 64 octets ; SuperSpeed, 512 octets.

Disposition d’un transfert de contrôle

Étant donné que les transferts de contrôle sont des transferts à haute priorité, une certaine quantité de bande passante est réservée sur le bus par l’hôte. Pour les appareils à basse vitesse et à pleine vitesse, 10 % de la bande passante ; 20 % pour les appareils de transfert high et SuperSpeed. Examinons maintenant la disposition d’un transfert de contrôle.

Diagramme d’un transfert de contrôle USB.

Un transfert de contrôle est divisé en trois transactions : transaction de configuration, transaction de données et transaction status. Chaque transaction contient trois types de paquets : paquet de jeton, paquet de données et paquet de négociation.

Certains champs sont communs à tous les paquets. Ces champs sont les suivants :

  • Champ de synchronisation qui indique le début du paquet.
  • Identificateur de paquet (PID) qui indique le type de paquet, la direction de la transaction et, dans le cas d’un paquet de négociation, il indique la réussite ou l’échec de la transaction.
  • Le champ EOP indique la fin du paquet.

Les autres champs dépendent du type de paquet.

Paquet de jetons

Chaque transaction d’installation commence par un paquet de jeton. Voici la structure du paquet. L’hôte envoie toujours le paquet de jeton.

Diagramme d’une disposition de paquet de jetons.

La valeur PID indique le type du paquet de jeton. Les valeurs possibles sont les suivantes :

  • SETUP : indique le début d’une transaction d’installation dans un transfert de contrôle.
  • IN : indique que l’hôte demande des données de l’appareil (cas de lecture).
  • OUT : indique que l’hôte envoie des données à l’appareil (cas d’écriture).
  • SOF : indique le début de l’image. Ce type de paquet de jeton contient un numéro d’image de 11 bits. L’hôte envoie le paquet SOF. La fréquence à laquelle ce paquet est envoyé dépend de la vitesse du bus. Pour une vitesse maximale, l’hôte envoie le paquet toutes les 1millisecondes ; toutes les 125 microsecondes sur un bus à grande vitesse.

Paquet de données

Immédiatement après le paquet de jeton se trouve le paquet de données qui contient la charge utile. Le nombre d’octets que chaque paquet de données peut contenir dépend de la taille de paquet maximale du point de terminaison par défaut. Le paquet de données peut être envoyé par l’hôte ou l’appareil, en fonction de la direction du transfert.

Diagramme d’une disposition de paquets de données.

Paquet de négociation

Immédiatement après le paquet de données se trouve le paquet de négociation. Le PID du paquet indique si le paquet a été reçu par l’hôte ou l’appareil. Le paquet de négociation peut être envoyé par l’hôte ou l’appareil, selon le sens du transfert.

Diagramme d’une disposition de paquet de négociation.

Vous pouvez voir la structure des transactions et des paquets à l’aide de n’importe quel analyseur USB, tel que Beagle, Ellisys, analyseurs de protocole USB LeCroy. Un appareil analyseur montre comment les données sont envoyées ou reçues à partir d’un périphérique USB via le câble. Dans cet exemple, examinons quelques traces capturées par un analyseur USB LeCroy. Cet exemple est à titre d’information uniquement. Il ne s’agit pas d’une approbation de Microsoft.

Transaction d’installation

L’hôte lance toujours un transfert de contrôle. Pour ce faire, il envoie une transaction d’installation. Cette transaction contient un paquet de jeton appelé jeton d’installation suivi d’un paquet de données de 8 octets. Cette capture d’écran montre un exemple de transaction d’installation.

Capture d’écran d’une trace d’une transaction d’installation.

Dans la trace précédente, l’hôte initie (indiqué par ) le transfert de contrôle en envoyant le paquet de jeton d’installation #434. Notez que le PID spécifie SETUP indiquant un jeton d’installation. Le PID est suivi de l’adresse de l’appareil et de l’adresse du point de terminaison. Pour les transferts de contrôle, cette adresse de point de terminaison est toujours 0.

Ensuite, l’hôte envoie le paquet de données #435. Le PID est DATA0 et cette valeur est utilisée pour le séquencement de paquets (à discuter). Le PID est suivi de 8 octets qui contiennent les informations main sur cette demande. Ces 8 octets indiquent le type de requête et la taille de la mémoire tampon dans laquelle l’appareil écrira sa réponse.

Tous les octets sont reçus dans l’ordre inverse. Comme décrit dans la section 9.3, nous voyons ces champs et valeurs :

Champ Taille Valeur Description
bmRequestType (voir 9.3.1 bmRequestType) 1 0x80 Le sens du transfert de données est de l’appareil à l’hôte (D7 est 1)

La demande est une requête standard (D6... D5 est 0)

Le destinataire de la demande est l’APPAREIL (D4 est 0)
bRequest (voir la section Voir 9.3.2 et le tableau 9-4) 1 0x06 Le type de requête est GET_DESCRIPTOR.
wValue (voir tableau 9-5) 2 0x0100 La valeur de la requête indique que le type de descripteur est DEVICE.
wIndex (voir la section 9.3.4) 2 0x0000 La direction est de l’hôte à l’appareil (D7 est 1)

Le numéro de point de terminaison est 0.
wLength (voir la section 9.3.5) 2 0x0012 La demande consiste à récupérer 18 octets.

Ainsi, nous pouvons conclure que dans ce transfert de contrôle (lecture), l’hôte envoie une demande de récupération du descripteur d’appareil et spécifie 18 octets comme longueur de transfert pour contenir ce descripteur. La façon dont l’appareil envoie ces 18 octets dépend de la quantité de données que le point de terminaison par défaut peut envoyer dans une transaction. Ces informations sont incluses dans le descripteur d’appareil retourné par l’appareil dans la transaction de données.

En réponse, l’appareil envoie un paquet de négociation (#436 indiqué par D.). Notez que la valeur PID est ACK (paquet ACK). Cela indique que l’appareil a reconnu la transaction.

Transaction de données

Voyons maintenant ce que l’appareil retourne en réponse à la demande. Les données réelles sont transférées dans une transaction de données.

Voici la trace de la transaction de données.

Capture d’écran montrant une trace d’un exemple de transaction de données.

Lors de la réception du paquet ACK, l’hôte lance la transaction de données. Pour lancer la transaction, il envoie un paquet de jeton (#450) avec la direction IN (appelé jeton IN).

En réponse, l’appareil envoie un paquet de données (#451) qui suit le jeton IN. Ce paquet de données contient le descripteur d’appareil réel. Le premier octet indique la longueur du descripteur d’appareil, 18 octets (0x12). Le dernier octet de ce paquet de données indique la taille de paquet maximale prise en charge par le point de terminaison par défaut. Dans ce cas, nous voyons que l’appareil peut envoyer 8 octets à la fois via son point de terminaison par défaut.

Notes

La taille maximale du paquet du point de terminaison par défaut dépend de la vitesse de l’appareil. Le point de terminaison par défaut d’un appareil à grande vitesse est de 64 octets ; l’appareil à faible vitesse est de 8 octets.

L’hôte reconnaît la transaction de données en envoyant un paquet ACK (#452) à l’appareil.

Calculons la quantité de données retournées. Dans le champ wLength du paquet de données (#435) dans la transaction d’installation, l’hôte a demandé 18 octets. Dans la transaction de données, nous voyons que seuls 8 premiers octets du descripteur d’appareil ont été reçus de l’appareil. Par conséquent, comment l’hôte reçoit-il les informations stockées dans les 10 octets restants ? L’appareil le fait en deux transactions : 8 octets, puis 2 derniers octets.

Maintenant que l’hôte connaît la taille de paquet maximale du point de terminaison par défaut, il lance une nouvelle transaction de données et demande la partie suivante en fonction de la taille du paquet.

Voici la prochaine transaction de données :

Capture d’écran montrant une trace de la nouvelle transaction de données.

L’hôte lance la transaction de données précédente en envoyant un jeton IN (#463) et en demandant les 8 octets suivants de l’appareil. L’appareil répond avec un paquet de données (#464) qui contient les 8 octets suivants du descripteur d’appareil.

À la réception des 8 octets, l’hôte envoie un paquet ACK (#465) à l’appareil.

Ensuite, l’hôte demande les 2 derniers octets d’une autre transaction de données comme suit :

Capture d’écran montrant une trace du nouvel exemple de transaction de données dans lequel l’hôte demande les 2 derniers octets.

Par conséquent, nous voyons que pour transférer 18 octets de l’appareil vers l’hôte, l’hôte effectue le suivi du nombre d’octets transférés et a lancé trois transactions de données (8+8+2).

Notes

Notez le PID des paquets de données dans les transactions de données 19, 23, 26. Le PID alterne entre DATA0 et DATA1. Cette séquence est appelée basculement de données. Dans les cas où il existe plusieurs transactions de données, la bascule des données est utilisée pour vérifier la séquence de paquets. Cette méthode garantit que les paquets de données ne sont pas dupliqués ou perdus.

En mappant les paquets de données consolidés à la structure du descripteur d’appareil (voir tableau 9-8), nous voyons les champs et valeurs suivants :

Champ Taille Valeur Description
bLength 1 0x12 Longueur du descripteur d’appareil, qui est de 18 octets.
bDescriptorType 1 0x01 Le type de descripteur est appareil.
bcdUSB 2 0x0100 Le numéro de version de la spécification est 1.00.
bDeviceClass 1 0x00 La classe d’appareil est 0. Chaque interface de la configuration contient les informations de classe.
bDeviceSubClass 1 0x00 La sous-classe est 0, car la classe d’appareil est 0.
bProtocol 1 0x00 Le protocole est 0. Cet appareil n’utilise aucun protocole spécifique à la classe.
bMaxPacketSize0 1 0x08 La taille de paquet maximale du point de terminaison est de 8 octets.
idVendor 2 0x0562 Communications télex.
idProduct 2 0x0002 Microphone USB.
bcdDevice 2 0x0100 Indique le numéro de mise en production de l’appareil.
iManufacturer 1 0x01 Chaîne du fabricant.
iProduct 1 0x02 Chaîne de produit.
iSerialNumber 1 0x03 Numéro de série.
bNumConfigurations 1 0x01 Nombre de configurations.

En examinant ces valeurs, nous disposons d’informations préliminaires sur l’appareil. L’appareil est un microphone USB à faible vitesse. La taille de paquet maximale du point de terminaison par défaut est de 8 octets. L’appareil prend en charge une configuration.

Transaction d’état

Enfin, l’hôte termine le transfert de contrôle en lançant la dernière transaction : status transaction.

Capture d’écran d’une trace d’un exemple de transaction de données.

L’hôte démarre la transaction avec un paquet de jeton OUT (#481). L’objectif de ce paquet est de vérifier que l’appareil a envoyé toutes les données demandées. Aucun paquet de données n’est envoyé dans cette transaction status. L’appareil répond avec un paquet ACK. Si une erreur s’est produite, le PID peut être NAK ou STALL.

Modèles de pilotes

Prérequis

Avant que le pilote client ne puisse énumérer les canaux, assurez-vous que ces conditions sont remplies :

  • Le pilote client doit avoir créé l’objet de périphérique cible USB du framework.

    Si vous utilisez les modèles USB fournis avec Microsoft Visual Studio Professional 2012, le code du modèle effectue ces tâches. Le code de modèle obtient le handle de l’objet d’appareil cible et les stocke dans le contexte de l’appareil.

Pilote client KMDF

Un pilote client KMDF doit obtenir un handle WDFUSBDEVICE en appelant la méthode WdfUsbTargetDeviceCreateWithParameters . Pour plus d’informations, consultez « Code source de l’appareil » dans Présentation de la structure de code du pilote client USB (KMDF).

Pilote client UMDF

Un pilote client UMDF doit obtenir un pointeur IWDFUsbTargetDevice en interrogeant l’objet d’appareil cible du framework. Pour plus d’informations, consultez « Implémentation IPnpCallbackHardware et tâches spécifiques à USB » dans Présentation de la structure de code du pilote client USB (UMDF).

L’aspect le plus important d’un transfert de contrôle consiste à mettre en forme le jeton d’installation de manière appropriée. Avant d’envoyer la demande, rassemblez cet ensemble d’informations :

  • Direction de la demande : hôte vers appareil ou appareil à hôte.
  • Destinataire de la demande : appareil, interface, point de terminaison ou autre.
  • Catégorie de requête : standard, classe ou fournisseur.
  • Type de requête, tel qu’une demande de GET_DESCRIPTPOR. Pour plus d’informations, consultez la section 9.5 de la spécification USB.
  • valeurs wValue et wIndex . Ces valeurs dépendent du type de requête.

Vous pouvez obtenir toutes ces informations à partir de la spécification USB officielle.

Si vous écrivez un pilote UMDF, obtenez le fichier d’en-tête, Usb_hw.h à partir de l’exemple de pilote UMDF pour le kit d’apprentissage OSR USB Fx2. Ce fichier d’en-tête contient des macros et une structure utiles pour la mise en forme du paquet d’installation pour le transfert de contrôle.

Tous les pilotes UMDF doivent communiquer avec un pilote en mode noyau pour envoyer et recevoir des données à partir d’appareils. Pour un pilote UMDF USB, le pilote en mode noyau est toujours le pilote fourni par Microsoft WinUSB (Winusb.sys).

Chaque fois qu’un pilote UMDF effectue une demande pour la pile de pilotes USB, le gestionnaire d’E/S Windows envoie la demande à WinUSB. Une fois la demande reçue, WinUSB traite la demande ou la transfère à la pile de pilotes USB.

Méthodes définies par Microsoft pour l’envoi de demandes de transfert de contrôle

Un pilote client USB sur l’hôte lance la plupart des demandes de contrôle pour obtenir des informations sur l’appareil, configurer l’appareil ou envoyer des commandes de contrôle du fournisseur. Toutes ces demandes peuvent être classées dans les catégories suivantes :

  • Les requêtes standard sont définies dans la spécification USB. L’objectif de l’envoi de requêtes standard est d’obtenir des informations sur l’appareil, ses configurations, ses interfaces et ses points de terminaison. Le destinataire de chaque requête dépend du type de requête. Le destinataire peut être l’appareil, une interface ou un point de terminaison.

    Notes

    La cible de tout transfert de contrôle est toujours le point de terminaison par défaut. Le destinataire est l’entité de l’appareil dont les informations (descripteur, status, etc.) intéressent l’hôte.

    Les demandes peuvent être classées davantage en : demandes de configuration, demandes de fonctionnalités et demandes de status.

    • Les demandes de configuration sont envoyées pour obtenir des informations à partir de l’appareil afin que l’hôte puisse le configurer, par exemple une demande de GET_DESCRIPTOR. Ces requêtes peuvent également être des demandes d’écriture envoyées par l’hôte pour définir une configuration particulière ou un autre paramètre dans l’appareil.
    • Les demandes de fonctionnalités sont envoyées par le pilote client pour activer ou désactiver certains paramètres d’appareil booléens pris en charge par l’appareil, l’interface ou un point de terminaison.
    • Les demandes d’état permettent à l’hôte d’obtenir ou de définir les bits status définis par USB d’un appareil, d’un point de terminaison ou d’une interface.

    Pour plus d’informations, consultez la section 9.4 de la spécification USB, version 2.0. Les types de requête standard sont définis le fichier d’en-tête, Usbspec.h.

  • Les demandes de classe sont définies par une spécification de classe d’appareil spécifique.

  • Les demandes du fournisseur sont fournies par le fournisseur et dépendent des demandes prises en charge par l’appareil.

La pile USB fournie par Microsoft gère toutes les communications de protocole avec l’appareil, comme indiqué dans les traces précédentes. Le pilote expose des interfaces de pilote de périphérique (DDIs) qui permettent à un pilote client d’envoyer des transferts de contrôle de plusieurs façons. Si votre pilote client est un pilote Windows Driver Foundation (WDF), il peut appeler des routines directement pour envoyer les types courants de demandes de contrôle. WDF prend en charge les transferts de contrôle intrinsèquement pour KMDF et UMDF.

Certains types de demandes de contrôle ne sont pas exposés via WDF. Pour ces demandes, le pilote client peut utiliser le modèle WDF-hybrid. Ce modèle permet au pilote client de générer et de mettre en forme des requêtes de style WDM URB, puis d’envoyer ces requêtes à l’aide d’objets de framework WDF. Le modèle hybride s’applique uniquement aux pilotes en mode noyau.

Pour les pilotes UMDF :

Utilisez les macros et la structure d’assistance définies dans usb_hw.h. Cet en-tête est inclus avec l’exemple de pilote UMDF pour le kit d’apprentissage FX2 USB OSR.

Utilisez ce tableau pour déterminer la meilleure façon d’envoyer des demandes de contrôle à la pile de pilotes USB. Si vous ne parvenez pas à afficher ce tableau, consultez le tableau de cet article.

Si vous souhaitez envoyer une demande de contrôle à... Pour un pilote KMDF... Pour un pilote UMDF... Pour un pilote WDM, générez une structure URB (routine d’assistance)
CLEAR_FEATURE : désactivez certains paramètres de fonctionnalité dans l’appareil, ses configurations, ses interfaces et ses points de terminaison. Consultez la section 9.4.1 de la spécification USB.
  1. Déclarez un paquet d’installation. Consultez la structure WDF_USB_CONTROL_SETUP_PACKET .
  2. Initialisez le paquet d’installation en appelant WDF_USB_CONTROL_SETUP_PACKET_INIT_FEATURE.
  3. Spécifiez une valeur de destinataire définie dans WDF_USB_BMREQUEST_RECIPIENT.
  4. Spécifiez le sélecteur de fonctionnalités (wValue). Consultez constantes USB_FEATURE_XXX dans Usbspec.h. Consultez également le tableau 9-6 dans la spécification USB.
  5. Définissez SetFeature surFALSE.
  6. Envoyez la demande en appelant WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.
  1. Déclarez un paquet d’installation. Consultez la structure WINUSB_CONTROL_SETUP_PACKET déclarée dans usb_hw.h.
  2. Initialisez le paquet d’installation en appelant la macro d’assistance , WINUSB_CONTROL_SETUP_PACKET_INIT_FEATURE, définie dans usb_hw.h.
  3. Spécifiez une valeur de destinataire définie dans WINUSB_BMREQUEST_RECIPIENT.
  4. Spécifiez le sélecteur de fonctionnalité (wValue). Consultez USB_FEATURE_XXX constantes dans Usbspec.h. Consultez également le tableau 9-6 dans la spécification USB.
  5. Définissez SetFeature surFALSE.
  6. Générez la requête en associant le paquet d’installation initialisé à l’objet de demande d’infrastructure et à la mémoire tampon de transfert en appelant la méthode IWDFUsbTargetDevice ::FormatRequestForControlTransfer .
  7. Envoyez la demande en appelant la méthode IWDFIoRequest ::Send .
_URB_CONTROL_FEATURE_REQUEST

(UsbBuildFeatureRequest)

URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE

URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE

URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT

URB_FUNCTION_CLEAR_FEATURE_TO_OTHER
GET_CONFIGURATION : Obtenir la configuration USB actuelle. Consultez la section 9.4.2 de la spécification USB. KMDF sélectionne la première configuration par défaut. Pour récupérer le numéro de configuration défini par l’appareil :

  1. Mettez en forme un WDF_USB_CONTROL_SETUP_PACKET et définissez son membre bRequestsur USB_REQUEST_GET_CONFIGURATION.
  2. Envoyez la requête en appelant WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.
UMDF sélectionne la première configuration par défaut. Pour récupérer le numéro de configuration défini par l’appareil :

  1. Déclarez un paquet d’installation. Consultez la structure WINUSB_CONTROL_SETUP_PACKET déclarée dans usb_hw.h.
  2. Initialisez le paquet d’installation en appelant la macro d’assistance, WINUSB_CONTROL_SETUP_PACKET_INIT, définie dans usb_hw.h.
  3. Spécifiez BmRequestToDevice comme direction, BmRequestToDevice comme destinataire et USB_REQUEST_GET_CONFIGURATION comme requête.
  4. Générez la requête en associant le paquet d’installation initialisé à l’objet de demande d’infrastructure et à la mémoire tampon de transfert en appelant la méthode IWDFUsbTargetDevice ::FormatRequestForControlTransfer .
  5. Envoyez la demande en appelant la méthode IWDFIoRequest ::Send .
  6. Recevez le numéro de configuration dans la mémoire tampon de transfert. Accédez à cette mémoire tampon en appelant les méthodes IWDFMemory .
_URB_CONTROL_GET_CONFIGURATION_REQUEST

URB_FUNCTION_GET_CONFIGURATION
GET_DESCRIPTOR : obtenir des descripteurs d’appareil, de configuration, d’interface et de point de terminaison. Consultez la section 9.4.3 de la spécification USB.

Pour plus d’informations, consultez Descripteurs USB.
Appelez ces méthodes :

Appelez ces méthodes :

_URB_CONTROL_DESCRIPTOR_REQUEST

(UsbBuildGetDescriptorRequest)

URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE

URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT

URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE
GET_INTERFACE : obtenez le paramètre alternatif actuel pour une interface. Consultez la section 9.4.4 de la spécification USB.

  1. Obtenez un handle WDFUSBINTERFACE pour l’objet d’interface cible en appelant la méthode WdfUsbTargetDeviceGetInterface .
  2. Appelez la méthode WdfUsbInterfaceGetConfiguredSettingIndex .
  1. Obtenez un pointeur IWDFUsbInterface vers l’objet d’interface cible.
  2. Appelez la méthode IWDFUsbInterface ::GetConfiguredSettingIndex .
_URB_CONTROL_GET_INTERFACE_REQUEST

URB_FUNCTION_GET_INTERFACE
GET_STATUS : obtenir status bits d’un appareil, d’un point de terminaison ou d’une interface. Voir la section 9.4.5. dans la spécification USB.
  1. Déclarez un paquet d’installation. Consultez la structure WDF_USB_CONTROL_SETUP_PACKET .
  2. Initialisez le paquet d’installation en appelant WDF_USB_CONTROL_SETUP_PACKET_INIT_GET_STATUS.
  3. Spécifiez la valeur de destinataire définie dans WDF_USB_BMREQUEST_RECIPIENT.
  4. Spécifiez les status que vous souhaitez obtenir : appareil, interface ou point de terminaison (wIndex).
  5. Envoyez la requête en appelant WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.
  1. Déclarez un paquet d’installation. Consultez la structure WINUSB_CONTROL_SETUP_PACKET déclarée dans usb_hw.h.
  2. Initialisez le paquet d’installation en appelant la macro d’assistance , WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS, définie dans usb_hw.h.
  3. Spécifiez une valeur de destinataire définie dans WINUSB_BMREQUEST_RECIPIENT.
  4. Spécifiez les status que vous souhaitez obtenir : appareil, interface ou point de terminaison (wIndex).
  5. Générez la requête en associant le paquet d’installation initialisé à l’objet de demande d’infrastructure et à la mémoire tampon de transfert en appelant la méthode IWDFUsbTargetDevice ::FormatRequestForControlTransfer .
  6. Envoyez la demande en appelant la méthode IWDFIoRequest ::Send .
  7. Recevez la valeur status dans la mémoire tampon de transfert. Accédez à cette mémoire tampon en appelant les méthodes IWDFMemory .
  8. Pour déterminer si la status indique une mise en éveil à distance auto-alimentée, utilisez les valeurs définies dans l’énumération WINUSB_DEVICE_TRAITS :
_URB_CONTROL_GET_STATUS_REQUEST

(UsbBuildGetStatusRequest)

URB_FUNCTION_GET_STATUS_FROM_DEVICE

URB_FUNCTION_GET_STATUS_FROM_INTERFACE

URB_FUNCTION_GET_STATUS_FROM_ENDPOINT

URB_FUNCTION_GET_STATUS_FROM_OTHER.
SET_ADDRESS : consultez la section 9.4.6 de la spécification USB. Cette requête est gérée par la pile de pilotes USB ; le pilote client ne peut pas effectuer cette opération. Cette requête est gérée par la pile de pilotes USB ; le pilote client ne peut pas effectuer cette opération. Cette requête est gérée par la pile de pilotes USB ; le pilote client ne peut pas effectuer cette opération.
SET_CONFIGURATION : définissez une configuration. Consultez la section 9.4.7 de la spécification USB.

Pour plus d’informations, consultez Comment sélectionner une configuration pour un périphérique USB.
Par défaut, KMDF sélectionne la configuration par défaut et le premier autre paramètre dans chaque interface. Le pilote client peut modifier la configuration par défaut en appelant la méthode WdfUsbTargetDeviceSelectConfigType et en spécifiant WdfUsbTargetDeviceSelectConfigTypeUrb comme option de requête. Vous devez ensuite mettre en forme un URB pour cette demande et l’envoyer à la pile de pilotes USB. Par défaut, UMDF sélectionne la configuration par défaut et le premier autre paramètre dans chaque interface. Le pilote client ne peut pas modifier la configuration. _URB_SELECT_CONFIGURATION

(USBD_SelectConfigUrbAllocateAndBuild)

URB_FUNCTION_SELECT_CONFIGURATION
SET_DESCRIPTOR : Mettez à jour un appareil, une configuration ou un descripteur de chaîne existant. Consultez la section 9.4.8 de la spécification USB.

Cette requête n’est pas couramment utilisée. Toutefois, la pile de pilotes USB accepte une telle demande du pilote client.
  1. Allouez et générez un URB pour la requête.
  2. Spécifiez les informations de transfert dans une structure de _URB_CONTROL_DESCRIPTOR_REQUEST .
  3. Envoyez la demande en appelant WdfUsbTargetDeviceFormatRequestForUrb ou WdfUsbTargetDeviceSendUrbSynchronously .
  1. Déclarez un paquet d’installation. Consultez la structure WINUSB_CONTROL_SETUP_PACKET déclarée dans usb_hw.h.
  2. Spécifiez les informations de transfert conformément à la spécification USB.
  3. Générez la requête en associant le paquet d’installation initialisé à l’objet de demande d’infrastructure et à la mémoire tampon de transfert en appelant la méthode IWDFUsbTargetDevice ::FormatRequestForControlTransfer .
  4. Envoyez la demande en appelant la méthode IWDFIoRequest ::Send .
_URB_CONTROL_DESCRIPTOR_REQUEST

URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE

URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT

URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE
SET_FEATURE : activez certains paramètres de fonctionnalité dans l’appareil, ses configurations, ses interfaces et ses points de terminaison. Consultez la section 9.4.9 de la spécification USB.
  1. Déclarez un paquet d’installation. Consultez la structure WDF_USB_CONTROL_SETUP_PACKET .
  2. Initialisez le paquet d’installation en appelant WDF_USB_CONTROL_SETUP_PACKET_INIT_FEATURE.
  3. Spécifiez la valeur du destinataire (appareil, interface, point de terminaison) définie dans WDF_USB_BMREQUEST_RECIPIENT.
  4. Spécifiez le sélecteur de fonctionnalité (wValue). Consultez USB_FEATURE_XXX constantes dans Usbspec.h. Consultez également le tableau 9-6 dans la spécification USB.
  5. Définissez SetFeature surTRUE
  6. Envoyez la requête en appelant WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.
  1. Déclarez un paquet d’installation. Consultez la structure WINUSB_CONTROL_SETUP_PACKET déclarée dans usb_hw.h.
  2. Initialisez le paquet d’installation en appelant la macro d’assistance, WINUSB_CONTROL_SETUP_PACKET_INIT_FEATURE, définie dans usb_hw.h.
  3. Spécifiez une valeur de destinataire définie dans WINUSB_BMREQUEST_RECIPIENT.
  4. Spécifiez le sélecteur de fonctionnalité (wValue). Consultez USB_FEATURE_XXX constantes dans Usbspec.h. Consultez également le tableau 9-6 dans la spécification USB.
  5. Définissez SetFeature surTRUE.
  6. Générez la requête en associant le paquet d’installation initialisé à l’objet de demande d’infrastructure et à la mémoire tampon de transfert en appelant la méthode IWDFUsbTargetDevice ::FormatRequestForControlTransfer .
  7. Envoyez la demande en appelant la méthode IWDFIoRequest ::Send .
_URB_CONTROL_FEATURE_REQUEST

(UsbBuildFeatureRequest)

URB_FUNCTION_SET_FEATURE_TO_DEVICE

URB_FUNCTION_SET_FEATURE_TO_INTERFACE

URB_FUNCTION_SET_FEATURE_TO_ENDPOINT

URB_FUNCTION_SET_FEATURE_TO_OTHER
SET_INTERFACE : modifie l’autre paramètre dans une interface. Consultez la section 9.4.9 de la spécification USB.

Pour plus d’informations, consultez Comment sélectionner un autre paramètre dans une interface USB.
WdfUsbTargetDeviceSelectConfig
  1. Obtenez un handle WDFUSBINTERFACE pour l’objet d’interface cible.
  2. Appelez la méthode WdfUsbInterfaceSelectSetting .
  1. Obtenez un pointeur IWDFUsbInterface vers l’objet d’interface cible.
  2. Appelez la méthode IWDFUsbInterface ::SelectSetting .
_URB_SELECT_INTERFACE

(USBD_SelectInterfaceUrbAllocateAndBuild)

URB_FUNCTION_SELECT_INTERFACE
SYNC_FRAME : définissez et obtenez le numéro de trame de synchronisation du point de terminaison. Consultez la section 9.4.10 de la spécification USB. Cette requête est gérée par la pile de pilotes USB ; le pilote client ne peut pas effectuer cette opération. Cette requête est gérée par la pile de pilotes USB ; le pilote client ne peut pas effectuer cette opération. Cette requête est gérée par la pile de pilotes USB ; le pilote client ne peut pas effectuer cette opération.
Pour les requêtes spécifiques à la classe d’appareil et les commandes du fournisseur.
  1. Déclarez un paquet d’installation. Consultez la structure WDF_USB_CONTROL_SETUP_PACKET .
  2. Initialisez le paquet d’installation en appelant des requêtes ou des WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR spécifiques à WDF_USB_CONTROL_SETUP_PACKET_INIT_CLASS pour les commandes du fournisseur.
  3. Spécifiez la valeur du destinataire (appareil, interface, point de terminaison) définie dans WDF_USB_BMREQUEST_RECIPIENT.
  4. Envoyez la requête en appelant WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.
  1. Déclarez un paquet d’installation. Consultez la structure WINUSB_CONTROL_SETUP_PACKET déclarée dans usb_hw.h.
  2. Initialisez le paquet d’installation en appelant la macro d’assistance, WINUSB_CONTROL_SETUP_PACKET_INIT_CLASS ou WINUSB_CONTROL_SETUP_PACKET_INIT_VENDOR, définie dans usb_hw.h.
  3. Spécifiez la direction (voir l’énumération WINUSB_BMREQUEST_DIRECTION ), le destinataire (voir l’énumération WINUSB_BMREQUEST_RECIPIENT ) et la requête, comme décrit dans la classe ou la spécification matérielle.
  4. Générez la requête en associant le paquet d’installation initialisé à l’objet de demande d’infrastructure et à la mémoire tampon de transfert en appelant la méthode IWDFUsbTargetDevice ::FormatRequestForControlTransfer .
  5. Envoyez la demande en appelant la méthode IWDFIoRequest ::Send .
  6. Recevez les informations de l’appareil dans la mémoire tampon de transfert. Accédez à cette mémoire tampon en appelant les méthodes IWDFMemory .
_URB_CONTROL_VENDOR_OR_CLASS_REQUEST

(UsbBuildVendorRequest)

URB_FUNCTION_VENDOR_DEVICE

URB_FUNCTION_VENDOR_INTERFACE

URB_FUNCTION_VENDOR_ENDPOINT

URB_FUNCTION_VENDOR_OTHER

URB_FUNCTION_CLASS_DEVICE

URB_FUNCTION_CLASS_INTERFACE

URB_FUNCTION_CLASS_ENDPOINT

URB_FUNCTION_CLASS_OTHER

Comment envoyer un transfert de contrôle pour les commandes du fournisseur - KMDF

Cette procédure montre comment un pilote client peut envoyer un transfert de contrôle. Dans cet exemple, le pilote client envoie une commande fournisseur qui récupère la version du microprogramme à partir de l’appareil.

  1. Déclarez une constante pour la commande vendor. Étudiez la spécification matérielle et déterminez la commande vendor que vous souhaitez utiliser.

  2. Déclarez une structure WDF_MEMORY_DESCRIPTOR et initialisez-la en appelant la macro WDF_MEMORY_DESCRIPTOR_INIT_BUFFER . Cette structure recevra la réponse du périphérique une fois que le pilote USB aura terminé la demande.

  3. Selon que vous envoyez la requête de manière synchrone ou asynchrone, spécifiez vos options d’envoi :

    • Si vous envoyez la requête de manière synchrone en appelant WdfUsbTargetDeviceSendControlTransferSynchronously, spécifiez une valeur de délai d’expiration. Cette valeur est importante, car sans délai d’expiration, vous pouvez bloquer le thread indéfiniment.

      Pour cela, déclarez une structure WDF_REQUEST_SEND_OPTIONS et initialisez-la en appelant la macro WDF_REQUEST_SEND_OPTIONS_INIT . Spécifiez l’option comme WDF_REQUEST_SEND_OPTION_TIMEOUT.

      Ensuite, définissez la valeur du délai d’expiration en appelant la macro WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT .

    • Si vous envoyez la requête de façon asynchrone, implémentez une routine d’achèvement. Libérez toutes les ressources allouées dans la routine d’achèvement.

  4. Déclarez une structure WDF_USB_CONTROL_SETUP_PACKET pour contenir le jeton d’installation et mettez en forme la structure. Pour ce faire, appelez la macro WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR pour mettre en forme le paquet d’installation. Dans l’appel, spécifiez la direction de la demande, le destinataire, les options de demande envoyée (initialisées à l’étape 3) et la constante de la commande vendor.

  5. Envoyez la requête en appelant WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.

  6. Vérifiez la valeur NTSTATUS retournée par l’infrastructure et examinez la valeur reçue.

Cet exemple de code envoie une demande de transfert de contrôle à un périphérique USB pour récupérer sa version du microprogramme. La requête est envoyée de manière synchrone et le pilote client spécifie une valeur de délai d’expiration relative de 5 secondes (en unités de 100 nanosecondes). Le pilote stocke la réponse reçue dans le contexte de périphérique défini par le pilote.

enum {
    USBFX2_GET_FIRMWARE_VERSION = 0x1,
....

} USBFX2_VENDOR_COMMANDS; 

#define WDF_TIMEOUT_TO_SEC              ((LONGLONG) 1 * 10 * 1000 * 1000)  // defined in wdfcore.h

const __declspec(selectany) LONGLONG
            DEFAULT_CONTROL_TRANSFER_TIMEOUT = 5 * -1 * WDF_TIMEOUT_TO_SEC; 


typedef struct _DEVICE_CONTEXT
{

    ...
       union {
        USHORT      VersionAsUshort;
        struct {
            BYTE Minor;
            BYTE Major;
        } Version;
    } Firmware; // Firmware version.

} DEVICE_CONTEXT, *PDEVICE_CONTEXT;


__drv_requiresIRQL(PASSIVE_LEVEL)
VOID  GetFirmwareVersion(
    __in PDEVICE_CONTEXT DeviceContext
)
{
    NTSTATUS                        status;
    WDF_USB_CONTROL_SETUP_PACKET    controlSetupPacket;
    WDF_REQUEST_SEND_OPTIONS        sendOptions;
    USHORT                          firmwareVersion;
    WDF_MEMORY_DESCRIPTOR           memoryDescriptor;

    PAGED_CODE();

    firmwareVersion = 0;

    WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, (PVOID) &firmwareVersion, sizeof(firmwareVersion));

    WDF_REQUEST_SEND_OPTIONS_INIT(
                                  &sendOptions,
                                  WDF_REQUEST_SEND_OPTION_TIMEOUT
                                  );

    WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(
                                         &sendOptions,
                                         DEFAULT_CONTROL_TRANSFER_TIMEOUT
                                         );

    WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket,
                                        BmRequestDeviceToHost,       // Direction of the request
                                        BmRequestToDevice,           // Recipient
                                        USBFX2_GET_FIRMWARE_VERSION, // Vendor command
                                        0,                           // Value
                                        0);                          // Index 

    status = WdfUsbTargetDeviceSendControlTransferSynchronously(
                                        DeviceContext->UsbDevice,
                                        WDF_NO_HANDLE,               // Optional WDFREQUEST
                                        &sendOptions,
                                        &controlSetupPacket,
                                        &memoryDescriptor,           // MemoryDescriptor
                                        NULL);                       // BytesTransferred 

    if (!NT_SUCCESS(status)) 
    {
        KdPrint(("Device %d: Failed to get device firmware version 0x%x\n", DeviceContext->DeviceNumber, status));
        TraceEvents(DeviceContext->DebugLog,
                    TRACE_LEVEL_ERROR,
                    DBG_RUN,
                    "Device %d: Failed to get device firmware version 0x%x\n",
                    DeviceContext->DeviceNumber,
                    status);
    }
    else 
    {
        DeviceContext->Firmware.VersionAsUshort = firmwareVersion;
        TraceEvents(DeviceContext->DebugLog,
                    TRACE_LEVEL_INFORMATION,
                    DBG_RUN,
                    "Device %d: Get device firmware version : 0x%x\n",
                    DeviceContext->DeviceNumber,
                    firmwareVersion);
    }

    return;
}

Comment envoyer un transfert de contrôle pour GET_STATUS - UMDF

Cette procédure montre comment un pilote client peut envoyer un transfert de contrôle pour une commande GET_STATUS. Le destinataire de la demande est l’appareil et la demande obtient des informations en bits D1-D0. Pour plus d’informations, consultez la figure 9-4 de la spécification USB.

  1. Incluez le fichier d’en-tête Usb_hw.h disponible avec l’exemple de pilote UMDF pour OSR USB Fx2 Learning Kit.

  2. Déclarez une structure WINUSB_CONTROL_SETUP_PACKET .

  3. Initialisez le paquet d’installation en appelant la macro d’assistance, WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS.

  4. Spécifiez BmRequestToDevice comme destinataire.

  5. Spécifiez 0 dans la valeur Index .

  6. Appelez la méthode d’assistance SendControlTransferSynchronously pour envoyer la requête de manière synchrone.

    La méthode d’assistance génère la requête en associant le paquet d’installation initialisé à l’objet de demande d’infrastructure et à la mémoire tampon de transfert en appelant la méthode IWDFUsbTargetDevice ::FormatRequestForControlTransfer . La méthode d’assistance envoie ensuite la demande en appelant la méthode IWDFIoRequest ::Send . Une fois la méthode retournée, inspectez la valeur retournée.

  7. Pour déterminer si la status indique une mise en éveil à distance auto-alimentée, utilisez les valeurs définies dans l’énumération WINUSB_DEVICE_TRAITS :

Cet exemple de code envoie une demande de transfert de contrôle à un obtenir le status de l’appareil. L’exemple envoie la requête de manière synchrone en appelant une méthode d’assistance nommée SendControlTransferSynchronously.

HRESULT
CDevice::GetDeviceStatus ()
{

    HRESULT hr = S_OK;

    USHORT deviceStatus;
    ULONG bytesTransferred;

    TraceEvents(TRACE_LEVEL_INFORMATION,
                DRIVER_ALL_INFO,
                "%!FUNC!: entry");

    // Setup the control packet.

    WINUSB_CONTROL_SETUP_PACKET setupPacket;

    WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS(
                                      &setupPacket,
                                      BmRequestToDevice,
                                      0);

    hr = SendControlTransferSynchronously(
                 &(setupPacket.WinUsb),
                 & deviceStatus,
                 sizeof(USHORT),
                 &bytesReturned
                );

     if (SUCCEEDED(hr))
    {
        if (deviceStatus & USB_GETSTATUS_SELF_POWERED)
        {
             m_Self_Powered = true;
        } 
        if (deviceStatus & USB_GETSTATUS_REMOTE_WAKEUP_ENABLED)
        {
             m_remote_wake-enabled = true;
        }
    }

    return hr;
 }

L’exemple de code suivant montre l’implémentation de la méthode d’assistance nommée SendControlTransferSynchronously. Cette méthode envoie une requête de manière synchrone.

HRESULT
CDevice::SendControlTransferSynchronously(
    _In_ PWINUSB_SETUP_PACKET SetupPacket,
    _Inout_ PBYTE Buffer,
    _In_ ULONG BufferLength,
    _Out_ PULONG LengthTransferred
    )
{
    HRESULT hr = S_OK;
    IWDFIoRequest *pWdfRequest = NULL;
    IWDFDriver * FxDriver = NULL;
    IWDFMemory * FxMemory = NULL;
    IWDFRequestCompletionParams * FxComplParams = NULL;
    IWDFUsbRequestCompletionParams * FxUsbComplParams = NULL;

    *LengthTransferred = 0;

    hr = m_FxDevice->CreateRequest( NULL, //pCallbackInterface
                                    NULL, //pParentObject
                                    &pWdfRequest);

    if (SUCCEEDED(hr))
    {
        m_FxDevice->GetDriver(&FxDriver);

        hr = FxDriver->CreatePreallocatedWdfMemory( Buffer,
                                                    BufferLength,
                                                    NULL,        //pCallbackInterface
                                                    pWdfRequest, //pParetObject
                                                    &FxMemory );
    }

    if (SUCCEEDED(hr))
    {
        hr = m_pIUsbTargetDevice->FormatRequestForControlTransfer( pWdfRequest,
                                                                   SetupPacket,
                                                                   FxMemory,
                                                                   NULL); //TransferOffset
    }

    if (SUCCEEDED(hr))
    {
        hr = pWdfRequest->Send( m_pIUsbTargetDevice,
                                WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
                                0); //Timeout
    }

    if (SUCCEEDED(hr))
    {
        pWdfRequest->GetCompletionParams(&FxComplParams);

        hr = FxComplParams->GetCompletionStatus();
    }

    if (SUCCEEDED(hr))
    {
        HRESULT hrQI = FxComplParams->QueryInterface(IID_PPV_ARGS(&FxUsbComplParams));
        WUDF_TEST_DRIVER_ASSERT(SUCCEEDED(hrQI));

        WUDF_TEST_DRIVER_ASSERT( WdfUsbRequestTypeDeviceControlTransfer ==
                            FxUsbComplParams->GetCompletedUsbRequestType() );

        FxUsbComplParams->GetDeviceControlTransferParameters( NULL,
                                                             LengthTransferred,
                                                             NULL,
                                                             NULL );
    }

    SAFE_RELEASE(FxUsbComplParams);
    SAFE_RELEASE(FxComplParams);
    SAFE_RELEASE(FxMemory);

    pWdfRequest->DeleteWdfObject(); 
    SAFE_RELEASE(pWdfRequest);

    SAFE_RELEASE(FxDriver);

    return hr;
}

Si vous utilisez Winusb.sys comme pilote de fonction pour votre appareil, vous pouvez envoyer des transferts de contrôle à partir d’une application. Pour mettre en forme le paquet d’installation dans WinUSB, utilisez les macros et structures d’assistance UMDF, décrites dans le tableau de cet article. Pour envoyer la demande, appelez WinUsb_ControlTransfer fonction.