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 uniques à 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, autrement dit, l’hôte peut envoyer des données au point de terminaison et recevoir des données à partir 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. Elle est active même avant la sélection d’une configuration.
  • La taille maximale des paquets du point de terminaison par défaut dépend de la vitesse de bus de l’appareil. Faible vitesse, 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 à priorité élevée, une certaine quantité de bande passante est réservée sur le bus par l’hôte. Pour les appareils à faible vitesse et pleine vitesse, 10 % de la bande passante ; 20 % pour les appareils de transfert haut 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 : la transaction d’installation, la transaction de données et la transaction d’état. Chaque transaction contient trois types de paquets : paquets de jetons, paquets de données et paquets 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 jetons. Voici la structure du paquet. L’hôte envoie toujours le paquet de jetons.

Diagramme d’une disposition de paquets de jetons.

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

  • PROGRAMME D’INSTALLATION : 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 à partir de l’appareil (cas en 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 nombre d’images 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 totale, 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, il s’agit du 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 maximale des paquets du point de terminaison par défaut. Le paquet de données peut être envoyé par l’hôte ou l’appareil, selon 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, il s’agit du 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 la direction du transfert.

Diagramme d’une disposition de paquets 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 à un appareil USB ou reçues via le câble. Dans cet exemple, examinons certaines traces capturées par un analyseur USB LeCroy. Cet exemple est destiné uniquement aux informations. Ce n’est pas une approbation par Microsoft.

Configuration de la transaction

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

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

Dans la trace précédente, l’hôte lance (indiqué par H>) le transfert de contrôle en envoyant le paquet de jetons d’installation #434. Notez que le PID spécifie le programme d’installation 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 principales relatives à cette demande. Ces 8 octets indiquent le type de requête et la taille de la mémoire tampon dans laquelle l’appareil écrit 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 9.3.2 et le tableau 9-4) 1 0x06 Le type de requête est GET_DESCRIPTOR.
wValue (Voir le 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 comprise entre l’hôte et 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.

Par conséquent, nous pouvons conclure que dans ce transfert de contrôle (lecture), l’hôte envoie une demande pour récupérer le descripteur de l’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

À présent, voyons 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 jetons (#450) avec la direction in (appelée 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 maximale de paquets prise en charge par le point de terminaison par défaut. Dans ce cas, nous constatons que l’appareil peut envoyer 8 octets à la fois via son point de terminaison par défaut.

Remarque

La taille maximale des paquets 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. Ainsi, comment l’hôte reçoit-il des informations stockées dans les 10 octets restants ? L’appareil le fait dans deux transactions : 8 octets, puis 2 derniers octets.

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

Voici la transaction de données suivante :

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 à partir de l’appareil. L’appareil répond avec un paquet de données (#464) qui contient les 8 octets suivants du descripteur de l’appareil.

Lors de 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 dans une autre transaction de données comme suit :

Capture d’écran montrant une trace de l’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 lancé trois transactions de données (8+8+2).

Remarque

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 bascule de données. Dans les cas où il existe plusieurs transactions de données, le basculement de données est utilisé 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 le tableau 9-8), nous voyons ces champs et valeurs :

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 maximale du paquet du point de terminaison est de 8 octets.
idVendor 2 0x0562 Telex Communications.
idProduct 2 0x0002 Microphone USB.
bcdDevice 2 0x0100 Indique le numéro de mise en production de l’appareil.
iManufacturer 1 0x01 Chaîne de 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 avons des informations préliminaires sur l’appareil. L’appareil est un microphone USB à faible vitesse. La taille maximale des paquets 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 : transaction d’état.

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 d’état. L’appareil répond avec un paquet ACK. Si une erreur s’est produite, le PID peut avoir été NAK ou STALL.

Modèles de pilotes

Prérequis

Avant que le pilote client puisse énumérer les canaux, vérifiez que ces exigences sont remplies :

  • Le pilote client doit avoir créé l'objet USB target device du framework.

    Si vous utilisez les modèles USB fournis avec Microsoft Visual Studio Professional 2012, le code de modèle effectue ces tâches. Le code du modèle obtient le handle de l'objet périphérique cible et le 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, voir "Code source de l'appareil" dans Comprendre la structure du 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 requête, rassemblez cet ensemble d’informations :

  • Direction de la demande : hôte vers l’appareil ou l’appareil à héberger.
  • 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 dans 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, récupérez le fichier d’en-tête, Usb_hw.h à partir du kit d’apprentissage UMDF Sample Driver pour OSR USB Fx2 Learning Kit. 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 USB UMDF, 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 requête à WinUSB. Après avoir reçu la requête, WinUSB traite la requête 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 requêtes standard sont définies dans la spécification USB. L’objectif de l’envoi de requêtes standard consiste à 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.

    Remarque

    La cible d’un 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, état, et ainsi de suite) de l’hôte sont intéressées.

    Les demandes peuvent être classées plus en : demandes de configuration, demandes de fonctionnalités et demandes d’état.

    • 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 demandes 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é 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 d’état 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 dans la spécification USB, version 2.0. Les types de requêtes standard sont définis dans 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 de 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 les 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 requêtes, le pilote client peut utiliser le modèle hybride WDF. Ce modèle permet au pilote client de générer et de mettre en forme des demandes de style URB WDM, 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 d’assistance et la structure 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 points de terminaison. Consultez la section 9.4.1 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_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 USB_FEATURE_XXX constantes dans Usbspec.h. Consultez également le tableau 9-6 dans la spécification USB.
  5. Définissez SetFeature sur FALSE.
  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és (wValue). Consultez USB_FEATURE_XXX constantes dans Usbspec.h. Consultez également le tableau 9-6 dans la spécification USB.
  5. Définissez SetFeature sur FALSE.
  6. Générez la requête en associant le paquet d’installation initialisé à l’objet de requête framework et à la mémoire tampon de transfert en appelant la méthode IWDFUsbTargetDevice ::FormatRequestForControlTransfer.
  7. Envoyez la requête 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 : obtenez la configuration USB actuelle. Consultez la section 9.4.2 dans 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 bRequest sur 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 requête framework et à la mémoire tampon de transfert en appelant la méthode IWDFUsbTargetDevice ::FormatRequestForControlTransfer.
  5. Envoyez la requête 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 dans la spécification USB.

Pour plus d’informations, consultez les 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 d’une interface. Consultez la section 9.4.4 dans la spécification USB.

  1. Obtenez un handle WDFUSBINTERFACE à 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 des bits d’état à partir d’un appareil, d’un point de terminaison ou d’une interface. Consultez 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 l’état à 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 l’état à 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 requête framework et à la mémoire tampon de transfert en appelant la méthode IWDFUsbTargetDevice ::FormatRequestForControlTransfer.
  6. Envoyez la requête en appelant la méthode IWDFIoRequest ::Send .
  7. Recevez la valeur d’état dans la mémoire tampon de transfert. Accédez à cette mémoire tampon en appelant les méthodes IWDFMemory.
  8. Pour déterminer si l’état indique l’auto-alimentation, le réveil à distance, 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 dans 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éfinir une configuration. Consultez la section 9.4.7 dans 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 paramètre de remplacement 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 une 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 paramètre de remplacement 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 descripteur d’appareil, de configuration ou de chaîne existant. Consultez la section 9.4.8 dans la spécification USB.

Cette demande 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 une URB pour la requête.
  2. Spécifiez les informations de transfert dans une structure de _URB_CONTROL_DESCRIPTOR_REQUEST .
  3. Envoyez la requête 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 requête framework et à la mémoire tampon de transfert en appelant la méthode IWDFUsbTargetDevice ::FormatRequestForControlTransfer.
  4. Envoyez la requête 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 points de terminaison. Consultez la section 9.4.9 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_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és (wValue). Consultez USB_FEATURE_XXX constantes dans Usbspec.h. Consultez également le tableau 9-6 dans la spécification USB.
  5. Définir SetFeature sur TRUE
  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és (wValue). Consultez USB_FEATURE_XXX constantes dans Usbspec.h. Consultez également le tableau 9-6 dans la spécification USB.
  5. Définissez SetFeature sur TRUE.
  6. Générez la requête en associant le paquet d’installation initialisé à l’objet de requête framework et à la mémoire tampon de transfert en appelant la méthode IWDFUsbTargetDevice ::FormatRequestForControlTransfer.
  7. Envoyez la requête 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 le paramètre de remplacement dans une interface. Consultez la section 9.4.9 dans 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 sur 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 d’image de synchronisation du point de terminaison. Consultez la section 9.4.10 dans 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 spécifiques à WDF_USB_CONTROL_SETUP_PACKET_INIT_CLASS ou WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR 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 requête framework et à la mémoire tampon de transfert en appelant la méthode IWDFUsbTargetDevice ::FormatRequestForControlTransfer.
  5. Envoyez la requête 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 de fournisseur qui récupère la version du microprogramme à partir de l’appareil.

  1. Déclarez une constante pour la commande fournisseur. Étudiez la spécification matérielle et déterminez la commande fournisseur 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 reçoit la réponse de l’appareil une fois que le pilote USB a terminé la demande.

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

  4. Déclarez une structure WDF_USB_CONTROL_SETUP_PACKET pour contenir le jeton d’installation et mettre 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 requête, le destinataire, les options de demande envoyée (initialisées à l’étape 3) et la constante de la commande fournisseur.

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

  6. Vérifiez la valeur NTSTATUS retournée par l’infrastructure et inspectez 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 façon 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 dans 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 d’index.

  6. Appelez la méthode d’assistance SendControlTransferSynchronously pour envoyer la requête de façon synchrone.

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

  7. Pour déterminer si l’état indique l’auto-alimentation, le réveil à distance, utilisez ces valeurs définies dans l’énumération WINUSB_DEVICE_TRAITS :

Cet exemple de code envoie une demande de transfert de contrôle à un état de l’appareil. L’exemple envoie la requête de façon 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 façon 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 requête, appelez WinUsb_ControlTransfer fonction.