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.
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.
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.
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.
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.
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.
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 :
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 :
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.
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
- Cadre du pilote en mode noyau
- Infrastructure du pilote en mode utilisateur
- Présentation de WinUSB pour les développeurs
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. |
|
|
_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 :
|
UMDF sélectionne la première configuration par défaut. Pour récupérer le numéro de configuration défini par l’appareil :
|
_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. |
|
_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. |
|
|
_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. |
|
|
_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. |
|
|
_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
|
|
_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. |
|
|
_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.
Déclarez une constante pour la commande fournisseur. Étudiez la spécification matérielle et déterminez la commande fournisseur que vous souhaitez utiliser.
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.
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 façon 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 de délai d’attente 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érer toutes les ressources allouées dans la routine d’achèvement.
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.
Envoyez la requête en appelant WdfUsbTargetDeviceSendControlTransferSynchronously ou WdfUsbTargetDeviceFormatRequestForControlTransfer.
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.
Incluez le fichier d’en-tête Usb_hw.h disponible avec l’exemple de pilote UMDF pour osR USB Fx2 Learning Kit.
Déclarez une structure WINUSB_CONTROL_SETUP_PACKET .
Initialisez le paquet d’installation en appelant la macro d’assistance, WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS.
Spécifiez BmRequestToDevice comme destinataire.
Spécifiez 0 dans la valeur d’index.
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.
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.