Comment transférer des données vers des points de terminaison isochroniques USB

Cette rubrique décrit comment un pilote client peut créer un bloc de requête USB (URB) pour transférer des données vers et à partir de points de terminaison isochronaux dans un périphérique USB.

Un périphérique USB (Universal Serial Bus) peut prendre en charge des points de terminaison isochronaux pour transférer des données dépendantes du temps à un rythme régulier, par exemple avec la diffusion en continu audio/vidéo. Pour transférer des données, le pilote client émet une demande de lecture ou d’écriture de données vers un point de terminaison isochronieux. Par conséquent, le contrôleur hôte lance un transfert isochronieux qui envoie ou reçoit des données en interrogeant l’appareil à intervalles réguliers.

Pour les appareils à grande vitesse et à vitesse maximale, l’interrogation est effectuée à l’aide de paquets de jetons (IN/OUT). Lorsque le point de terminaison est prêt à envoyer des données, l’appareil répond à l’un des paquets de jetons IN en envoyant des données. Pour écrire sur l’appareil, le contrôleur hôte envoie un paquet de jeton OUT suivi de paquets de données. Le contrôleur hôte ou l’appareil n’envoie pas de paquets de négociation, et par conséquent, aucune remise n’est garantie. Étant donné que le contrôleur hôte ne tente pas de réessayer le transfert, les données peuvent être perdues en cas d’erreur.

Pour les transferts isochroniques, le contrôleur hôte réserve certaines périodes de temps sur le bus. Pour gérer le temps réservé pour les points de terminaison isochroniques, le temps est divisé en chucks logiques consécutifs appelés intervalles de bus. L’unité d’intervalle de bus dépend de la vitesse du bus.

Pour une vitesse maximale, un intervalle de bus est un cadre. La longueur d’une image est de 1 milliseconde.

Pour la vitesse élevée et la vitesse SuperSpeed, l’intervalle de bus est un microframe. La longueur d’un microframe est de 125 microsecondes. Huit microframes consécutifs constituent une image haute vitesse ou SuperSpeed.

Les transferts isochronieux sont basés sur des paquets. Le terme paquet isochroneux dans cette rubrique fait référence à la quantité de données transférées dans un intervalle de bus. Les caractéristiques du point de terminaison déterminent la taille de chaque paquet est fixe et déterminée par les caractéristiques du point de terminaison.

Le pilote client démarre un transfert isochronieux en créant un URB pour la demande et en soumettant l’URB à la pile de pilotes USB. La requête est gérée par l’un des pilotes inférieurs de la pile de pilotes USB. Lors de la réception de l’URB, la pile de pilotes USB effectue un ensemble de validations et planifie les transactions pour la demande. Pour une vitesse maximale, un paquet isochronique à transférer dans chaque intervalle de bus est contenu dans une seule transaction sur le câble. Certains appareils à grande vitesse autorisent plusieurs transactions dans un intervalle de bus. Dans ce cas, le pilote client peut envoyer ou recevoir plus de données dans le paquet isochronieux dans une requête unique (URB). Les appareils SuperSpeed prennent en charge plusieurs transactions et transferts en rafale, ce qui permet d’obtenir encore plus d’octets par intervalle de bus. Pour plus d’informations sur les transferts en rafale, consultez la page de spécification USB 3.0 9-42.

Avant de commencer

Avant de créer une demande de transfert isochronieux, vous devez disposer d’informations sur le canal ouvert pour le point de terminaison isochronieux.

Un pilote client qui utilise des routines WDM (Windows Driver Model) a les informations de canal dans l’une des structures USBD_PIPE_INFORMATION d’un tableau de USBD_INTERFACE_LIST_ENTRY . Le pilote client a obtenu ce tableau dans la demande précédente du pilote pour sélectionner une configuration ou une interface dans l’appareil.

Un pilote client WDF (Windows Driver Framework) doit obtenir une référence à l’objet de canal cible de l’infrastructure et appeler WdfUsbTargetPipeGetInformation pour obtenir des informations de canal dans une structure WDF_USB_PIPE_INFORMATION .

En fonction des informations de canal, déterminez cet ensemble d’informations :

  • Quantité de données que le contrôleur hôte peut envoyer au canal dans chaque paquet.

    La quantité de données que le pilote client peut envoyer dans une requête ne peut pas dépasser le nombre maximal d’octets que le contrôleur hôte peut envoyer ou recevoir à partir d’un point de terminaison. Le nombre maximal d’octets est indiqué par le membre MaximumPacketSize des structures USBD_PIPE_INFORMATION et WDF_USB_PIPE_INFORMATION . La pile de pilotes USB définit la valeur MaximumPacketSize lors d’une demande select-configuration ou select-interface.

    Pour les appareils à pleine vitesse, MaximumPacketSize est dérivé des 11 premiers bits du champ wMaxPacketSize du descripteur de point de terminaison, qui indique le nombre maximal d’octets que le point de terminaison peut envoyer ou recevoir dans une transaction. Pour les appareils à pleine vitesse, le contrôleur envoie une transaction par intervalle de bus.

    Dans un transfert isochronique à grande vitesse, le contrôleur hôte peut envoyer des transactions supplémentaires dans un intervalle de bus si le point de terminaison les autorise. Le nombre de transactions supplémentaires est défini par l’appareil et indiqué dans les bits 12..11 du wMaxPacketSize. Ce nombre peut être 0, 1 ou 2. Si 12..11 indique 0, les transactions supplémentaires par microframe ne sont pas prises en charge par le point de terminaison. Si le nombre est 1, le contrôleur hôte peut envoyer une transaction supplémentaire (deux transactions au total par microframe) ; 2 indique deux transactions supplémentaires (au total trois transactions par microframe). La valeur MaximumPacketSize définie par la pile de pilotes USB inclut le nombre d’octets pouvant être envoyés dans des transactions supplémentaires.

    Pour le transfert isochronique SuperSpeed, certaines valeurs de USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR (voir Usbspec.h) sont importantes. La pile de pilotes USB utilise ces valeurs pour calculer le nombre maximal d’octets dans un intervalle de bus.

    • Champ Isochronous.Mult du descripteur de compagnon du point de terminaison. Dans les transferts isochrons SuperSpeed, les transactions supplémentaires (tout comme les appareils à haut débit) sont appelées transactions de rafale. La valeur Mult indique le nombre maximal de transactions de rafale que le point de terminaison prend en charge. Il peut y avoir jusqu’à trois transactions de rafale (indexées de 0 à 2) dans un intervalle de service.

    • Champ bMaxBurst du descripteur complémentaire du point de terminaison. Cette valeur indique le nombre de segments de wMaxPacketSize qui peuvent être présents dans une transaction de rafale unique. Il peut y avoir jusqu’à 16 segments (indexés de 0 à 15) dans une transaction de rafale.

    • wBytesPerInterval indique le nombre total d’octets que l’hôte peut envoyer ou recevoir dans un intervalle de bus. Même si le nombre maximal d’octets par intervalle de bus peut être calculé comme (bMaxBurst+1) * (Mult+1) * wMaxPacketSize, la spécification USB 3.0 recommande d’utiliser la valeur wBytesPerInterval à la place. La valeur wBytesPerInterval doit être inférieure ou égale à cette valeur calculée.

      Important

      Pour un pilote client, les valeurs décrites dans le précédent sont à des fins d’information uniquement. Le pilote doit toujours utiliser la valeur MaximumPacketSize du descripteur de point de terminaison pour déterminer la disposition de la mémoire tampon de transfert.

  • À quelle fréquence le point de terminaison envoie-t-il ou reçoit-il des données ?

    Le membre Interval est utilisé pour déterminer la fréquence à laquelle le point de terminaison peut envoyer ou recevoir des données. L’appareil définit cette valeur et le pilote client ne peut pas la modifier. La pile de pilotes USB utilise un autre nombre pour déterminer la fréquence à laquelle elle insère des paquets isochrone dans le flux de données : la période d’interrogation, qui est dérivée de la valeur Interval .

    Pour les transmissions à pleine vitesse, les valeurs d’intervalle et de période d’interrogation sont toujours 1 ; la pile de pilotes USB ignore les autres valeurs.

    Le tableau suivant montre l’intervalle et la période d’interrogation calculée pour les transferts à grande vitesse et SuperSpeed :

    Intervalle Période d’interrogation (2Interval-1)
    1 1; Les données sont transférées à chaque intervalle de bus.
    2 2; Les données sont transférées tous les deux intervalles de bus.
    3 4; Les données sont transférées tous les quatre intervalles de bus.
    4 8; Les données sont transférées tous les huit intervalles de bus.
  • Quelles sont les restrictions sur le nombre de paquets pour chaque vitesse de bus.

    Dans un URB, vous ne pouvez envoyer que jusqu’à 255 paquets isochroniques pour un appareil à pleine vitesse ; 1 024 paquets dans un URB pour les appareils haute vitesse et SuperSpeed. Le nombre de paquets que vous envoyez dans l’URB doit être un multiple du nombre de paquets dans chaque image.

    Période d’interrogation Nombre de paquets pour la vitesse élevée/SuperSpeed
    1 Multiple de 8
    2 Multiple de 4
    3 Multiple de 2
    4 Quelconque

Prenons un exemple de point de terminaison à pleine vitesse avec wMaxPacketSize 1 023. Pour cet exemple, l’application a fourni une mémoire tampon de 25 575 octets. Le transfert de cette mémoire tampon nécessite 25 paquets isochroux (25575/1023).

Prenons un exemple de point de terminaison à grande vitesse avec les caractéristiques suivantes indiquées dans le descripteur de point de terminaison.

  • wMaxPacketSize est 1 024.
  • Les bits 12..11 indiquent deux transactions supplémentaires.
  • L’intervalle est 1.

Une fois que le pilote client a sélectionné une configuration, MaximumPacketSize pour le canal isochronieux indique 3 072 octets (total des transactions * wMaxPacketSize). Les transactions supplémentaires permettent au pilote client de transférer 3 072 octets dans chaque microframe, pour un total de 24 576 octets en une seule image. L’illustration suivante montre la fréquence à laquelle un paquet isochroneux est transféré dans un microframe pour les transmissions à haut débit.

Diagramme des intervalles de transfert isochronieux, des périodes d’interrogation et des paquets.

Considérez un exemple de point de terminaison SuperSpeed avec les caractéristiques indiquées dans les descripteurs complémentaires du point de terminaison et du point de terminaison SuperSpeed :

  • wMaxPacketSize est 1 024.
  • bMaxBurst est 15.
  • L’intervalle est 1.
  • Isochronous.Mult est 2.
  • wBytesPerInterval est 45000.

Dans l’exemple précédent, même si le nombre maximal d’octets peut être calculé en tant que wMaxPacketSize * (bMaxBurst +1) * (Mult + 1), ce qui donne 49 152 octets, l’appareil limite la valeur à la valeur wBytesPerInterval qui est de 45 000 octets. Cette valeur est également reflétée dans MaximumPacketSize 45 000. Le pilote client doit uniquement utiliser la valeur MaximumPacketSize . Dans cet exemple, la demande peut être divisée en trois transactions de rafale. Les deux premières transactions de rafale contiennent chacune 16 blocs de wMaxPacketSize. La dernière transaction de rafale contient 12 blocs pour contenir les octets restants. Cette image montre l’intervalle d’interrogation et les octets transférés via un paquet isochronisé pour la transmission SuperSpeed.

Diagramme des intervalles de transfert isochroniques à grande vitesse, des périodes d’interrogation et des paquets.

Pour générer une demande de transfert isochronieux :

  1. Obtenez la taille de chaque paquet isochrone.
  2. Déterminez le nombre de paquets isochroneux par image.
  3. Calculez le nombre de paquets isochronieux requis pour contenir l’intégralité de la mémoire tampon de transfert.
  4. Allouez une structure URB pour décrire les détails du transfert.
  5. Spécifiez les détails de chaque paquet isochronisé, comme le décalage de paquet.

Pour obtenir un exemple de code complet sur l’envoi de demandes de transfert isochrone, USBSAMP.

Cet exemple de cette rubrique simplifie l’implémentation USBSAMP du transfert isochrone. L’exemple calcule le nombre total d’images requises pour le transfert. En fonction de la quantité de données pouvant être envoyées dans un cadre, la mémoire tampon de transfert est divisée en octets de taille plus petite.

La procédure suivante développe les étapes précédentes et affiche les calculs et les routines qu’un pilote client peut utiliser pour générer et envoyer une demande de transfert isochroneuse pour un point de terminaison isochronique à grande vitesse. Les valeurs utilisées dans la procédure sont basées sur les exemples de caractéristiques de point de terminaison décrits précédemment.

Étape 1 : Obtenir la taille d’un paquet isochrone

Déterminez la taille d’un paquet isochrone en inspectant la valeur MaximumPacketSize du canal.

Pour les transmissions à pleine vitesse, la taille d’un paquet isochrone correspond au nombre d’octets que vous pouvez transférer en une seule image. Pour les transmissions haute vitesse et SuperSpeed, la taille d’un paquet isochrone correspond au nombre total d’octets pouvant être transférés dans un microframe. Ces valeurs sont indiquées dans maximumPacketSize du canal.

Dans l’exemple, MaximumPacketSize est de 1 023 octets par image (pleine vitesse) ; 3 072 octets par microframe (vitesse élevée) ; 45 000 octets par microframe (SuperSpeed).

Notes

La valeur MaximumPacketSize indique la taille maximale autorisée du paquet isochrone. Le pilote client peut définir la taille de chaque paquet isochrone sur n’importe quelle valeur inférieure à la valeur MaximumPacketSize .

Étape 2 : Déterminer le nombre de paquets isochrone par image

Pour les transmissions à pleine vitesse, vous transférez un paquet isochroneux dans chaque image.

Pour les transmissions à grande vitesse et SuperSpeed, cette valeur doit être dérivée de la valeur Interval. Dans l’exemple, Intervalle est 1. Par conséquent, le nombre de paquets isochronieux doit être de huit par image. Pour obtenir d’autres valeurs Interval, consultez le tableau de la section Prérequis.

Étape 3 : Calculer le nombre de paquets isochroniques requis pour contenir l’intégralité de la mémoire tampon de transfert

Calculez le nombre de paquets isochronieux requis pour transférer l’intégralité de la mémoire tampon. Cette valeur peut être calculée en divisant la longueur de la mémoire tampon de transfert par la taille d’un paquet isochrone.

Dans cet exemple, nous partons du principe que la taille de chaque paquet isochrone est MaximumPacketSize et que la longueur de la mémoire tampon de transfert est un multiple de la valeur MaximumPacketSize .

Par exemple, pour un transfert à pleine vitesse, une mémoire tampon fournie de 25 575 octets nécessite 25 paquets isochronieux (25575/1023). Pour le transfert à haut débit, une mémoire tampon de taille 24 576 est divisée en huit paquets isochrons (24576/3072) pour le transfert. Pour SuperSpeed, une mémoire tampon de 360 000 octets s’intègre dans huit paquets isochrone (360000/450000).

Le pilote client doit valider les exigences suivantes :

  • Le nombre de paquets isochronieux doit être un multiple du nombre de paquets par image.
  • Le nombre maximal de paquets isochroneuses requis pour effectuer le transfert ne doit pas dépasser 255 pour un appareil à pleine vitesse ; 1024 pour un appareil haute vitesse ou SuperSpeed.

Étape 4 : Allouer une structure URB pour décrire les détails du transfert

  1. Allouez une structure URB dans un pool non paginé.

    Si votre pilote client utilise des routines WDM, le pilote doit appeler le USBD_IsochUrbAllocate si vous disposez du Kit de pilotes Windows (WDK) pour Windows 8. Un pilote client peut utiliser la routine pour cibler Windows Vista et les versions ultérieures du système d’exploitation Windows. Si vous n’avez pas le WDK pour Windows 8 ou si le pilote client est destiné à une version antérieure du système d’exploitation, vous pouvez allouer la structure sur la pile ou dans un pool non paginé en appelant ExAllocatePoolWithTag.

    Un pilote client WDF peut appeler la méthode WdfUsbTargetDeviceCreateIsochUrb pour allouer de la mémoire pour la structure URB .

  2. Le membre UrbIsochronousTransfer de la structure URB pointe vers une structure _URB_ISOCH_TRANSFER qui décrit les détails d’un transfert isochrone. Initialisez les membres UrbIsochronousTransfer suivants comme suit :

    • Définissez le membre UrbIsochronousTransfer.Hdr.Length sur la taille de l’URB. Pour obtenir la taille de l’URB, appelez GET_ISO_URB_SIZE macro et spécifiez le nombre de paquets.

    • Définissez le membre UrbIsochronousTransfer.Hdr.Function sur URB_FUNCTION_ISOCH_TRANSFER.

    • Définissez le membre UrbIsochronousTransfer.NumberOfPackets sur le nombre de paquets isochroneux.

    • Définissez UrbIsochronousTransfer.PipeHandle sur le handle opaque pour le canal associé au point de terminaison. Assurez-vous que la poignée de canal est la poignée de canal USBD utilisée par la pile de pilotes USB (Universal Serial Bus).

      Pour obtenir le handle de canal USBD, un pilote client WDF peut appeler la méthode WdfUsbTargetPipeWdmGetPipeHandle et spécifier le handle WDFUSBPIPE à l’objet de canal de l’infrastructure. Un pilote client WDM doit utiliser le même handle que celui obtenu dans le membre PipeHandle de la structure USBD_PIPE_INFORMATION .

    • Spécifiez la direction du transfert. Définissez UrbIsochronousTransfer.TransferFlags sur USBD_TRANSFER_DIRECTION_IN pour un transfert IN isochrone (lecture à partir de l’appareil) ; USBD_TRANSFER_DIRECTION_OUT pour un transfert OUT isochronieux (écriture sur l’appareil).

    • Spécifiez l’indicateur USBD_START_ISO_TRANSFER_ASAP dans UrbIsochronousTransfer. TransferFlags. L’indicateur indique à la pile de pilotes USB d’envoyer le transfert dans le cadre approprié suivant. Pour la première fois que le pilote client envoie un URB isochronieux pour ce canal, la pile de pilotes envoie les paquets isochroniques dans l’URB dès qu’elle le peut. La pile de pilotes USB suit le cadre suivant à utiliser pour les URI suivants sur ce canal. En cas de retard dans l’envoi d’un URB isochronieux ultérieur qui utilise l’indicateur USBD_START_ISO_TRANSFER_ASAP, la pile de pilotes considère que tout ou partie des paquets de cette URB sont en retard et ne transfère pas ces paquets.

      La pile de pilotes USB réinitialise son USBD_START_ISO_TRANSFER_ASAP suivi des images de démarrage, si la pile ne reçoit pas d’URB isochronieux pour 1024 images après avoir terminé l’URB précédente pour ce canal. Au lieu de spécifier l’indicateur USBD_START_ISO_TRANSFER_ASAP, vous pouvez spécifier le cadre de début. Pour plus d'informations, consultez la section Notes.

    • Spécifiez la mémoire tampon de transfert et sa taille. Vous pouvez définir un pointeur vers la mémoire tampon dans UrbIsochronousTransfer.TransferBuffer ou le MDL qui décrit la mémoire tampon dans UrbIsochronousTransfer.TransferBufferMDL.

      Pour récupérer le MDL pour la mémoire tampon de transfert, un pilote client WDF peut appeler WdfRequestRetrieveOutputWdmMdl ou WdfRequestRetrieveInputWdmMdl, selon le sens du transfert.

Étape 5 : Spécifier les détails de chaque paquet isochronisé dans le transfert

La pile de pilotes USB alloue la nouvelle structure URB suffisamment grande pour contenir des informations sur chaque paquet isochronisé, mais pas sur les données contenues dans le paquet. Dans la structure URB , le membre UrbIsochronousTransfer.IsoPacket est un tableau de USBD_ISO_PACKET_DESCRIPTOR qui décrit les détails de chaque paquet isochrone dans le transfert. Les paquets doivent être contigus. Le nombre d’éléments dans le tableau doit correspondre au nombre de paquets isochroneux spécifiés dans le membre UrbIsochronousTransfer.NumberOfPackets de l’URB.

Pour un transfert à grande vitesse, chaque élément du tableau est corrélé à un paquet isochroneux dans un microframe. Pour une vitesse maximale, chaque élément est corrélé à un paquet isochroneux transféré dans une image.

Pour chaque élément, spécifiez le décalage d’octet de chaque paquet isochronisé à partir du début de la mémoire tampon de transfert entière pour la requête. Vous pouvez spécifier cette valeur en définissant UrbIsochronousTransfer.IsoPacket[i]. Membre offset . La pile de pilotes USB utilise la valeur spécifiée pour suivre la quantité de données à envoyer ou à recevoir.

Définition du décalage pour un transfert Full-Speed

Pour l’exemple, il s’agit des entrées de tableau pour la mémoire tampon de transfert à pleine vitesse. À pleine vitesse, le pilote client dispose d’une image pour transférer un paquet isochronieux jusqu’à 1 023 octets. Une mémoire tampon de transfert de 25 575 octets peut contenir 25 paquets isochronieux, chacun de 1 023 octets de long. Un total de 25 images sont nécessaires pour l’ensemble de la mémoire tampon.

Frame 1 IsoPacket [0].Offset = 0 (start address)
Frame 2 IsoPacket [1].Offset = 1023
Frame 3 IsoPacket [2].Offset = 2046
Frame 4 IsoPacket [3].Offset = 3069
...
Frame 25 IsoPacket [24].Offset = 24552

Total length transferred is 25,575 bytes.

Définition du décalage pour un transfert High-Speed

Pour l’exemple, il s’agit des entrées de tableau d’une mémoire tampon de transfert à haute vitesse. L’exemple suppose que la mémoire tampon est de 24 576 octets et que le pilote client dispose d’une trame pour transférer huit paquets isochroques, chacun de 3 072 octets.

Microframe 1 IsoPacket [0].Offset = 0 (start address)
Microframe 2 IsoPacket [1].Offset = 3072
Microframe 3 IsoPacket [2].Offset = 6144
Microframe 4 IsoPacket [3].Offset = 9216
Microframe 5 IsoPacket [4].Offset = 12288
Microframe 6 IsoPacket [5].Offset = 15360
Microframe 7 IsoPacket [6].Offset = 18432
Microframe 8 IsoPacket [7].Offset = 21504

Total length transferred is 24,576 bytes.

Définition du décalage d’un transfert SuperSpeed

Pour l’exemple, il s’agit du décalage matricielle pour SuperSpeed. Vous pouvez transférer jusqu’à 45 000 octets dans une image. La mémoire tampon de transfert d’une taille de 360 000 tient dans huit microframes.

Microframe 1 IsoPacket [0].Offset = 0 (start address)
Microframe 2 IsoPacket [1].Offset = 45000
Microframe 3 IsoPacket [2].Offset = 90000
Microframe 4 IsoPacket [3].Offset = 135000
Microframe 5 IsoPacket [4].Offset = 180000
Microframe 6 IsoPacket [5].Offset = 225000
Microframe 7 IsoPacket [6].Offset = 270000
Microframe 8 IsoPacket [7].Offset = 315000

Total length transferred is 360,000 bytes.

UrbIsochronousTransfer.IsoPacket[i]. Le membre Length n’implique pas la longueur de chaque paquet de l’URB isochroneuse. IsoPacket[i]. La longueur est mise à jour par la pile de pilotes USB pour indiquer le nombre réel d’octets reçus de l’appareil pour les transferts IN isochronous. Pour les transferts OUT isochronous, la pile de pilotes ignore la valeur définie dans IsoPacket[i]. Longueur.

Spécifier le numéro de trame USB de départ pour le transfert

Le membre UrbIsochronousTransfer.StartFrame de l’URB spécifie le numéro de trame USB de départ pour le transfert. Il y a toujours une latence entre le moment où le pilote client envoie un URB et le moment où la pile de pilotes USB traite l’URB. Par conséquent, le pilote client doit toujours spécifier une trame de démarrage postérieure à la trame actuelle lorsque le pilote envoie l’URB. Pour récupérer le numéro d’image actuel, le pilote client peut envoyer la requête URB_FUNCTION_GET_CURRENT_FRAME_NUMBER à la pile de pilotes USB (_URB_GET_CURRENT_FRAME_NUMBER).

Pour les transferts isochroques, la différence absolue entre l’image actuelle et la valeur StartFrame doit être inférieure à USBD_ISO_START_FRAME_RANGE. Si StartFrame ne se trouve pas dans la plage appropriée, la pile de pilotes USB définit le membre Status de l’en-tête URB (voir _URB_HEADER) sur USBD_STATUS_BAD_START_FRAME et ignore l’intégralité de l’URB.

La valeur StartFrame spécifiée dans l’URB indique le numéro de trame dans lequel le premier paquet isochroneuse de l’URB est transféré. Le numéro de trame des paquets suivants dépend de la vitesse du bus et des valeurs de période d’interrogation du point de terminaison. Par exemple, pour une transmission à pleine vitesse, le premier paquet est transféré dans StartFrame ; Le deuxième paquet est transféré dans StartFrame+1, et ainsi de suite. La façon dont la pile de pilotes USB transfère les paquets isochroques, pour une vitesse maximale, dans les images est illustrée comme suit :

Frame (StartFrame)   IsoPacket [0]
Frame (StartFrame+1) IsoPacket [1]
Frame (StartFrame+2) IsoPacket [2]
Frame (StartFrame+3) IsoPacket [3]
...

Pour un appareil haut débit avec la valeur Interval de 1, le numéro de trame change tous les huit microframes. La façon dont la pile de pilotes USB transfère les paquets isochrons, pour une vitesse élevée, dans les images est illustrée comme suit :

Frame (StartFrame) Microframe 1 IsoPacket [0]
...
Frame (StartFrame) Microframe 8 IsoPacket [7]
Frame (StartFrame+1) Microframe 1 IsoPacket [8]
...
Frame (StartFrame+1) Microframe 8 IsoPacket [15]
Frame (StartFrame+2) Microframe 1 IsoPacket [16]
...
Frame (StartFrame+2) Microframe 8 IsoPacket [23]

Lorsque la pile de pilotes USB traite l’URB, le pilote ignore tous les paquets isochroneuses dans l’URB dont le nombre de trames est inférieur au numéro de trame actuel. La pile de pilotes définit le membre Status du descripteur de paquets pour chaque paquet ignoré sur USBD_STATUS_ISO_NA_LATE_USBPORT, USBD_STATUS_ISO_NOT_ACCESSED_BY_HW ou USBD_STATUS_ISO_NOT_ACCESSED_LATE. Même si certains paquets de l’URB sont ignorés, la pile de pilotes tente de transmettre uniquement les paquets dont le nombre de trames est supérieur au numéro de trame actuel.

La case activée d’un membre StartFrame valide est légèrement plus compliquée dans les transmissions à grande vitesse, car la pile de pilotes USB charge chaque paquet isochroneuse dans un microframe haute vitesse. Toutefois, la valeur dans StartFrame fait référence au nombre d’images de 1 milliseconde (pleine vitesse) et non au microframe. Par exemple, si la valeur StartFrame enregistrée dans l’URB est inférieure à une valeur inférieure à la trame actuelle, la pile de pilotes peut ignorer autant que huit paquets. Le nombre exact de paquets ignorés dépend de la période d’interrogation associée au canal isochronous.

Exemple de transfert isochronous

L’exemple de code suivant montre comment créer un URB pour un transfert isochroque pour une transmission à pleine vitesse, haute vitesse et SuperSpeed.

#define MAX_SUPPORTED_PACKETS_FOR_HIGH_OR_SUPER_SPEED 1024
#define MAX_SUPPORTED_PACKETS_FOR_FULL_SPEED 255

NTSTATUS CreateIsochURB  ( PDEVICE_OBJECT         DeviceObject,
                          PUSBD_PIPE_INFORMATION  PipeInfo,
                          ULONG                   TotalLength,
                          PMDL                    RequestMDL,
                          PURB                    Urb)
{
    PDEVICE_EXTENSION        deviceExtension;
    ULONG                    numberOfPackets;
    ULONG                    numberOfFrames;
    ULONG                    isochPacketSize = 0;
    ULONG                    transferSizePerFrame;
    ULONG                    currentFrameNumber;
    size_t                   urbSize;
    ULONG                    index;
    NTSTATUS                 ntStatus;

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    isochPacketSize = PipeInfo->MaximumPacketSize;

    // For high-speed transfers
    if (deviceExtension->IsDeviceHighSpeed || deviceExtension->IsDeviceSuperSpeed)
    {
        // Ideally you can pre-calculate numberOfPacketsPerFrame for the Pipe and
        // store it in the pipe context.

        switch (PipeInfo->Interval)
        {
        case 1:
            // Transfer period is every microframe (eight times a frame).
            numberOfPacketsPerFrame = 8;
            break;

        case 2:
            // Transfer period is every 2 microframes (four times a frame).
            numberOfPacketsPerFrame = 4;
            break;

        case 3:
            // Transfer period is every 4 microframes (twice in a frame).
            numperOfPacketsPerFrame = 2;
            break;

        case 4:
        default:
            // Transfer period is every 8 microframes (once in a frame).
            numberOfPacketsPerFrame = 1;
            break;
        }

        //Calculate the number of packets.
        numberOfPackets = TotalLength / isochPacketSize;

        if (numberOfPackets > MAX_SUPPORTED_PACKETS_FOR_HIGH_OR_SUPER_SPEED)
        {
            // Number of packets cannot be  greater than 1021.
            ntStatus = STATUS_INVALID_PARAMETER;
            goto Exit;
        }

        if (numberOfPackets % numberOfPacketsPerFrame != 0)
        {

            // Number of packets should be a multiple of numberOfPacketsPerFrame
            ntStatus = STATUS_INVALID_PARAMETER;
            goto Exit;
        }

    }
    else if (deviceExtension->IsDeviceFullSpeed)
    {
        //For full-speed transfers
        // Microsoft USB stack only supports bInterval value of 1 for
        // full-speed isochronous endpoints.

        //Calculate the number of packets.
        numberOfPacketsPerFrame = 1;

        numberOfPackets = TotalLength / isochPacketSize;

        if (numberOfPackets > MAX_SUPPORTED_PACKETS_FOR_FULL_SPEED)
        {
            // Number of packets cannot be greater than 255.
            ntStatus = STATUS_INVALID_PARAMETER;
            goto Exit;
        }
    }

    // Allocate an isochronous URB for the transfer
    ntStatus = USBD_IsochUrbAllocate (deviceExtension->UsbdHandle,
        numberOfPackets,
        &Urb);

    if (!NT_SUCCESS(ntStatus))
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto Exit;
    }

    urbSize = GET_ISO_URB_SIZE(numberOfPackets);

    Urb->UrbIsochronousTransfer.Hdr.Length = (USHORT) urbSize;
    Urb->UrbIsochronousTransfer.Hdr.Function = URB_FUNCTION_ISOCH_TRANSFER;
    Urb->UrbIsochronousTransfer.PipeHandle = PipeInfo->PipeHandle;

    if (USB_ENDPOINT_DIRECTION_IN(PipeInfo->EndpointAddress))
    {
        Urb->UrbIsochronousTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_IN;
    }
    else
    {
        Urb->UrbIsochronousTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_OUT;
    }

    Urb->UrbIsochronousTransfer.TransferBufferLength = TotalLength;
    Urb->UrbIsochronousTransfer.TransferBufferMDL = RequestMDL;
    Urb->UrbIsochronousTransfer.NumberOfPackets = numberOfPackets;
    Urb->UrbIsochronousTransfer.UrbLink = NULL;

    // Set the offsets for every packet for reads/writes

    for (index = 0; index < numberOfPackets; index++)
    {
        Urb->UrbIsochronousTransfer.IsoPacket[index].Offset = index * isochPacketSize;
    }

    // Length is a return value for isochronous IN transfers.
    // Length is ignored by the USB driver stack for isochronous OUT transfers.

    Urb->UrbIsochronousTransfer.IsoPacket[index].Length = 0;
    Urb->UrbIsochronousTransfer.IsoPacket[index].Status = 0;

    // Set the USBD_START_ISO_TRANSFER_ASAP. The USB driver stack will calculate the start frame.
    // StartFrame value set by the client driver is ignored.
    Urb->UrbIsochronousTransfer.TransferFlags |= USBD_START_ISO_TRANSFER_ASAP;

Exit:

    return ntStatus;
}