Comment récupérer des erreurs de canal USB
Remarque
Cet article s'adresse aux développeurs de pilotes d'appareils. Si vous rencontrez des difficultés avec un appareil USB, veuillez consulter Corriger les problèmes liés à l'USB-C dans Windows.
Cet article fournit des informations sur les étapes que vous pouvez essayer lorsqu'un transfert de données vers un tube USB échoue. Les mécanismes décrits dans cet article couvrent les opérations d'abandon, de réinitialisation et de cycle du port sur les canaux en vrac, d'interruption et isochrones.
Un pilote client USB communique avec son appareil en envoyant des transferts de contrôle au point de terminaison par défaut ; des transferts de données aux points de terminaison en vrac, d'interruption et isochrones de l'appareil. Parfois, ces transferts peuvent échouer pour diverses raisons, comme une condition de décrochage dans le point de terminaison. Si le transfert échoue, le canal associé ne peut pas traiter les requêtes jusqu'à ce que la condition d'erreur soit levée.
Pour les transferts de contrôle, la pile du pilote USB supprime automatiquement les conditions d'erreur. Pour les transferts de données, le client doit prendre les mesures appropriées pour se remettre de la condition d'erreur. Lorsqu'un transfert de données échoue, la pile du pilote USB signale l'erreur au pilote client par le biais de codes d'état USBD d'échec. En fonction du code d'état, le pilote peut alors fournir un mécanisme de récupération d'erreur.
Cet article fournit des indications sur la récupération des erreurs par le biais de ces opérations.
- Réinitialiser le canal USB
- Réinitialisez le port USB auquel l'appareil est connecté
- Effectuez un cycle sur le port USB pour ré-énumérer la pile de périphériques pour le pilote client.
Pour effacer une condition d'erreur, commencez par l'opération reset-canal et n'effectuez des opérations plus complexes, telles que reset-port et cycle-port, qu'en cas de nécessité.
À propos de la coordination des différents mécanismes de récupération :
Le pilote client doit coordonner les différentes opérations de récupération et s'assurer qu'une seule méthode est utilisée à un moment donné. Prenons l'exemple d'un appareil doté de deux points de terminaison : un bulk et une interruption. Après avoir envoyé quelques requêtes de transfert de données à l'appareil, le pilote remarque que les requêtes échouent sur le canal bulk. Pour remédier à ces erreurs, le pilote réinitialise la conduite de masse. Cependant, cette opération ne résout pas les erreurs de transfert et les transferts en bloc continuent d'échouer. Le pilote émet donc une requête de réinitialisation du port USB. Pendant ce temps, les transferts commencent à échouer sur le canal d'interruption, puis une requête de réinitialisation de l'appareil est émise. Pour remédier aux échecs des transferts d'interruption, le pilote émet une requête de réinitialisation de la conduite d'interruption. Si ces deux opérations ne sont pas coordonnées, le pilote peut lancer deux opérations de réinitialisation d'appareil simultanément, en raison d'échecs sur les deux canaux. Ces opérations simultanées peuvent être problématiques.
Le pilote client doit s'assurer qu'à un moment donné, il n'effectue qu'une seule opération de réinitialisation de port ou de cycle de port. Pendant ces opérations, aucune opération de réinitialisation de canal ne doit être en cours sur un canal et le pilote ne doit pas émettre de nouvelle requête de réinitialisation de canal.
Bon à savoir
Cet article utilise le cadre des pilotes en mode noyau (KMDF).
Prérequis
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 du 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.
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).
Le pilote client doit disposer d'un handle sur l'objet canal cible du framework. Pour plus d'informations, voir Comment énumérer les canaux USB.
Étape 1 : Déterminer la cause de l'erreur
Le pilote client initie un transfert de données en utilisant un bloc de requête USB (URB). Une fois la requête terminée, la pile du pilote USB renvoie un code d'état USBD qui indique si le transfert a réussi ou échoué. En cas d'échec, le code USBD indique la raison de l'échec.
- Si vous avez envoyé l'URB en appelant la méthode WdfUsbTargetDeviceSendUrbSynchronously, vérifiez le membre Hdr.Status de la structure URB après le retour de la méthode.
- Si vous avez envoyé l'URB de manière asynchrone en appelant la méthode WdfRequestSend, vérifiez le statut de l'URB dans l'EVT_WDF_REQUEST_COMPLETION_ROUTINE. Le paramètre Params pointe vers une structure WDF_REQUEST_COMPLETION_PARAMS. Pour vérifier le code d'état de l'USBD, inspectez le membre Usb->UsbdStatus. Pour plus d'informations sur le code, voir USBD_STATUS.
Les échecs de transfert peuvent résulter d'une erreur d'appareil, telle que USBD_STATUS_STALL_PID ou USBD_STATUS_BABBLE_DETECTED. Ils peuvent également résulter d'une erreur signalée par le contrôleur hôte, telle que USBD_STATUS_XACT_ERROR.
Étape 2 : Déterminer si l'appareil est connecté au port
Avant d'émettre une requête qui réinitialise le canal ou l'appareil, assurez-vous que l'appareil est connecté. Vous pouvez déterminer l'état connecté de l'appareil en appelant la méthode WdfUsbTargetDeviceIsConnectedSynchronous.
Étape 3 : Annulez tous les transferts en attente vers le canal
Avant d'envoyer des requêtes qui réinitialisent le canal ou le port, annulez toutes les requêtes de transfert en attente vers le canal, que la pile du pilote USB n'a pas encore terminées. Vous pouvez annuler les requêtes de l'une des manières suivantes :
Arrêtez la cible d'E/S en appelant la méthode WdfIoTargetStop.
Pour arrêter la cible d'E/S, obtenez d'abord la poignée WDFIOTARGET associée à l'objet canal du cadre en appelant la méthode WdfUsbTargetPipeGetIoTarget. En utilisant le handle, appelez WdfIoTargetStop. Dans l'appel, définissez l'action sur WdfIoTargetCancelSentIo (voir WDF_IO_TARGET_SENT_IO_ACTION)** pour demander au cadre d'annuler toutes les requêtes que la pile du pilote USB n'a pas terminées. Pour les requêtes qui ont été complétées, le pilote client doit attendre que son callback d'achèvement soit invoqué par le framework.
Envoyez une requête "abort-canal". Vous pouvez envoyer la requête en appelant l'une de ces méthodes :
Appelez la méthode WdfUsbTargetPipeAbortSynchronously.
L'appel est synchrone et ne revient qu'après l'annulation de toutes les requêtes en attente. WdfUsbTargetPipeAbortSynchronously prend un paramètre optionnel Request. Nous vous recommandons de transmettre un handle WDFREQUEST à un objet de requête préalloué du framework. Ce paramètre permet au cadre d'utiliser l'objet de requête spécifié au lieu d'un objet de requête interne auquel le pilote ne peut pas accéder. La valeur de ce paramètre garantit que WdfUsbTargetPipeAbortSynchronously n'échoue pas en raison d'une mémoire insuffisante.
Appelez la méthode WdfUsbTargetPipeFormatRequestForAbort pour formater un objet de requête pour une requête abort-canal, puis envoyez la requête en appelant la méthode WdfRequestSend.
Si le pilote envoie la requête de manière asynchrone, il doit spécifier un pointeur vers la EVT_WDF_REQUEST_COMPLETION_ROUTINE que le pilote implémente. Pour spécifier le pointeur, appelez la méthode WdfRequestSetCompletionRoutine.
Le pilote peut envoyer la requête de manière synchrone en spécifiant WDF_REQUEST_SEND_OPTION_SYNCHRONOUS comme l'une des options de requête dans WdfRequestSend. Si vous envoyez la requête de manière synchrone, appelez plutôt WdfUsbTargetPipeAbortSynchronously.
Étape 4 : Réinitialisez le canal USB
Commencez la récupération de l'erreur en réinitialisant le canal. Vous pouvez envoyer une requête de réinitialisation du canal en appelant l'une de ces méthodes :
Appelez la méthode WdfUsbTargetPipeResetSynchronously pour envoyer une requête de réinitialisation du canal de manière synchrone.
Appelez la méthode WdfUsbTargetPipeFormatRequestForReset pour formater un objet de requête pour une requête de réinitialisation du canal, puis envoyez la requête en appelant la méthode WdfRequestSend. Ces appels sont similaires à ceux de la requête de réinitialisation du canal, comme décrit à l'étape 3.
Remarque
N'envoyez pas de nouvelles requêtes de transfert tant que l'opération de réinitialisation du pipeline n'est pas terminée.
La requête de réinitialisation du tube efface la condition d'erreur dans l'appareil et dans le matériel du contrôleur hôte. Pour effacer l'erreur du périphérique, la pile du pilote USB envoie une requête de contrôle CLEAR_FEATURE à l'appareil en utilisant le sélecteur de fonctionnalité ENDPOINT_HALT. Le destinataire de la requête est le point de terminaison associé au canal. Si la condition d'erreur s'est produite sur un canal isochrone, la pile du pilote ne prend aucune mesure pour effacer l'appareil car, en cas d'erreur, les points de terminaison isochrones sont effacés automatiquement.
Pour effacer l'erreur du contrôleur hôte, la pile du pilote efface l'état HALT de la conduite et remet à 0 le basculement des données de la conduite.
Étape 5 : Réinitialisation du port USB
Si une opération de réinitialisation de la conduite n'efface pas la condition d'erreur et que les transferts de données continuent d'échouer, envoyez une requête de réinitialisation du port.
Annulez tous les transferts vers l'appareil. Pour ce faire, énumérez tous les canaux de la configuration actuelle et annulez les requêtes en attente programmées pour chaque canal.
Arrêtez la cible d'E/S pour l'appareil.
Appelez la méthode WdfUsbTargetDeviceGetIoTarget pour obtenir un handle WDFIOTARGET associé à l'objet de l'appareil cible du cadre. Ensuite, appelez WdfIoTargetStop et spécifiez le handle WDFIOTARGET. Dans l'appel, définissez l'action sur WdfIoTargetCancelSentIo (WDF_IO_TARGET_SENT_IO_ACTION).
Envoyez une requête de réinitialisation du port en appelant la méthode WdfUsbTargetDeviceResetPortSynchronously.
Une opération de réinitialisation du port entraîne la réinscription de l'appareil sur le bus USB. La pile du pilote USB préserve la configuration de l'appareil après l'énumération. Le pilote client peut utiliser les poignées de canaux obtenues précédemment car la pile du pilote garantit que les poignées de canaux existantes restent valides.
Vous ne pouvez pas réinitialiser une fonction individuelle d'un appareil composite. Pour un appareil composite, lorsque le pilote client d'une fonction particulière envoie une requête de réinitialisation du port, l'appareil entier est réinitialisé. Si l'appareil USB conserve un état, cette requête de réinitialisation du port peut affecter les pilotes clients d'autres fonctions. Il est donc important que le pilote client tente de réinitialiser le canal avant de réinitialiser le port.
Étape 6 : Cycle du port USB
Une opération de cycle du port est similaire à l'appareil qui est débranché et rebranché sur le port, sauf que l'appareil n'est pas déconnecté électriquement. L'appareil est déconnecté et reconnecté par logiciel. Cette opération entraîne la réinitialisation et l'énumération de l'appareil. En conséquence, le gestionnaire PnP reconstruit le nœud de l'appareil.
Si une opération de réinitialisation du port n'efface pas la condition d'erreur et que les transferts de données continuent d'échouer, envoyez une requête de port de cycle.
Annulez tous les transferts vers l'appareil. Veillez à annuler les requêtes en attente programmées pour chaque canal dans la configuration actuelle (voir l'étape 3).
Arrêtez la cible d'E/S pour l'appareil.
Appelez la méthode WdfUsbTargetDeviceGetIoTarget pour obtenir un handle WDFIOTARGET associé à l'objet de l'appareil cible du cadre. Ensuite, appelez WdfIoTargetStop et spécifiez le handle WDFIOTARGET. Dans l'appel, définissez l'action sur WdfIoTargetCancelSentIo (WDF_IO_TARGET_SENT_IO_ACTION).
Envoyez une requête de port de cycle en appelant l'une des méthodes suivantes :
- Appelez la méthode WdfUsbTargetDeviceCyclePortSynchronously pour envoyer une requête de port de cycle de manière synchrone.
- Appelez la méthode WdfUsbTargetDeviceFormatRequestForCyclePort pour formater un objet de requête pour une requête de port de cycle, puis envoyez la requête en appelant la méthode WdfRequestSend. Ces appels sont similaires à ceux de la requête de réinitialisation du canal, comme décrit à l'étape 3.
Le pilote client ne peut envoyer des requêtes de transfert à l'appareil qu'une fois la requête de port cyclique terminée. En effet, le nœud de l'appareil est supprimé pendant que la pile du pilote USB traite la requête de port cyclique.
La requête de port cyclique entraîne une nouvelle énumération de l'appareil. La pile du pilote USB informe le gestionnaire PnP que l'appareil a été déconnecté. Le gestionnaire PnP détruit la pile de périphériques associée au pilote client. La pile du pilote réinitialise l'appareil, le ré-énumère sur le bus USB et informe le gestionnaire PnP qu'un appareil a été connecté. Le gestionnaire PnP reconstruit alors la pile de périphériques pour l'appareil USB.
À la suite d'une opération de port cyclique, toute application ayant une poignée ouverte sur l'appareil reçoit une notification de retrait de l'appareil (si l'application s'est enregistrée pour recevoir une telle notification). En réponse, l'application peut envoyer un message de déconnexion de l'appareil à l'utilisateur. Parce qu'il a un impact sur l'expérience de l'utilisateur, le pilote client ne doit opter pour une requête cycle-port que si les autres mécanismes de récupération ne permettent pas de résoudre la condition d'erreur.
Comme pour l'opération de réinitialisation du port (décrite à l'étape 6), pour un appareil composite, l'opération cycle-port affecte l'ensemble de l'appareil et non les fonctions individuelles de l'appareil.