Suspension sélective dans les pilotes de fonction KMDF USB

Cet article explique comment les pilotes de fonctions KMDF prennent en charge l'interruption sélective USB.

Si le pilote USB nécessite des fonctionnalités ou des ressources qui ne sont pas disponibles en mode utilisateur, vous devez fournir un pilote de fonction KMDF. Les pilotes KMDF implémentent une suspension sélective en définissant des valeurs pertinentes dans une structure d’initialisation KMDF, puis en fournissant les fonctions de rappel appropriées. KMDF gère les détails de la communication avec les drivers de niveau inférieur pour suspendre et reprendre le fonctionnement de l'appareil.

Lignes directrices pour la suspension sélective dans les pilotes KMDF

Les pilotes KMDF qui prennent en charge la suspension sélective doivent suivre les instructions suivantes :

  • Un pilote de fonction KMDF doit être le PPO de sa pile de périphériques. Par défaut, les pilotes de fonction KMDF sont définis comme les PPO.
  • Un pilote de fonction KMDF qui prend en charge la suspension sélective peut utiliser des files d’attente qui sont gérées par l’alimentation ou des files d’attente qui ne sont pas gérées par l’alimentation. Par défaut, les objets de file d’attente pour PPOs sont gérés par la gestion de l’alimentation.

Propriété de la stratégie d’alimentation et pilotes USB KMDF

Par défaut, le pilote de fonction KMDF pour un périphérique USB est l’objet PPO de la pile de périphériques. KMDF gère la suspension sélective et la reprise pour le compte de ce pilote.

Configuration de file d’attente d’E/S dans les pilotes KMDF

Un pilote de fonction KMDF qui prend en charge la suspension sélective peut utiliser des files d’attente qui sont sous gestion de l'alimentation ou des files d’attente qui ne le sont pas. En règle générale, un pilote configure une file d’attente qui n’est pas gérée par l’alimentation pour recevoir des demandes de contrôle d’E/S d’appareil entrantes et configure une ou plusieurs files d’attente gérées par l’alimentation pour recevoir des demandes de lecture, d’écriture et d’autres demandes dépendantes de l’alimentation. Lorsqu’une demande arrive à une file d’attente gérée par l’alimentation, KMDF garantit que l’appareil est en D0 avant de présenter la demande au pilote.

Si vous écrivez un pilote de filtre KMDF superposé au-dessus de l’objet PPO dans la pile de périphériques, vous ne devez pas utiliser de files d'attente gérées par l'alimentation. La raison est la même que pour les pilotes UMDF. L’infrastructure ne présente pas de demandes provenant de files d’attente gérées par l’alimentation pendant que l’appareil est suspendu, de sorte que l’utilisation de ces files d’attente peut bloquer la pile de l’appareil.

Mécanisme de suspension sélectif pour les pilotes de fonction KMDF

KMDF gère la plupart du travail nécessaire pour prendre en charge la suspension sélective USB. Il effectue le suivi de l’activité d’E/S, gère le minuteur d’inactivité et envoie les demandes de contrôle d’E/S de l’appareil qui provoquent la suspension et la reprise du pilote parent (Usbhub.sys ou Usbccgp.sys).

Si un pilote de fonction KMDF prend en charge la suspension sélective, KMDF effectue le suivi de l’activité d’E/S sur toutes les files d’attente gérées par l’alimentation que possède chaque objet de périphérique. Le framework démarre un minuteur d’inactivité chaque fois que le nombre d’E/S atteint zéro. La valeur de délai d’attente par défaut est de 5 secondes.

Si une demande d’E/S arrive à une file d’attente à gestion d’énergie qui appartient à l'objet de périphérique avant l’expiration du délai d’inactivité, le framework annule le minuteur d’inactivité et ne suspend pas le périphérique.

Lorsque le minuteur d’inactivité expire, KMDF émet les demandes requises pour placer l’appareil USB dans l’état suspendu. Si un pilote de fonction utilise un lecteur continu sur un point de terminaison USB, l’interrogation répétée du lecteur ne compte pas comme activité pour le minuteur d’inactivité KMDF. Toutefois, dans la fonction de rappel EvtDeviceD0Exit , le pilote USB doit arrêter manuellement le lecteur continu et les autres cibles d’E/S qui sont alimentées par des files d’attente qui ne sont pas gérées par l’alimentation pour s’assurer que le pilote n’envoie pas de demandes d’E/S pendant que l’appareil n’est pas dans l’état de fonctionnement. Pour arrêter les cibles, le pilote appelle WdfIoTargetStop et spécifie WdfIoTargetWaitForSentIoToComplete comme action cible. En réponse, le cadre arrête la cible d’E/S uniquement une fois que toutes les demandes d’E/S qui se trouvent dans la file d’attente d’E/S de la cible ont été terminées et que les rappels d’achèvement d’E/S associés ont été exécutés.

Par défaut, KMDF transfère le périphérique hors de l'état D0 et dans l'état d'alimentation du périphérique que le pilote a spécifié dans les paramètres d’inactivité. Dans le cadre de la transition, KMDF appelle les fonctions de rappel d’alimentation du pilote de la même façon que pour toute autre séquence de mise hors tension.

Une fois l’appareil suspendu, l’infrastructure reprend automatiquement l’appareil quand l’un des événements suivants se produit :

Pour reprendre l’appareil, KMDF envoie une demande d’alimentation vers le bas de la pile d’appareils, puis appelle les fonctions de rappel du pilote de la même façon que pour toute autre séquence de mise sous tension.

Pour plus d’informations sur les fonctions de rappel impliquées dans les séquences de mise hors tension et de mise sous tension, consultez le livre blanc Plug-and-Play et Gestion de l’alimentation dans les pilotes WDF.

Prise en charge de la suspension sélective USB dans un pilote de fonction KMDF

Pour implémenter une suspension sélective USB dans un pilote de fonction KMDF :

  • initialisez les paramètres de stratégie d'alimentation liés à l'inactivité, y compris le délai d'attente en cas d'inactivité.
  • Si vous le souhaitez, incluez une logique pour empêcher temporairement l’opération de suspension ou de reprise lorsque le pilote détermine que l’appareil ne doit pas être suspendu en raison d’un handle ouvert ou d’une autre raison qui n’est pas liée aux files d’attente d’E/S de l’appareil.
  • Dans un pilote USB pour un périphérique à interface humaine (HID), indiquez dans l'INF qu’il prend en charge la suspension sélective.

Initialisation des paramètres de stratégie d’alimentation dans un pilote de fonction KMDF

Pour configurer la prise en charge de la suspension sélective USB, un pilote KMDF utilise la structure WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS. Le pilote doit d’abord initialiser la structure, puis définir des champs qui fournissent des détails sur les fonctionnalités du pilote et de son appareil. En règle générale, le pilote remplit cette structure dans sa fonction EvtDriverDeviceAdd ou EvtDevicePrepareHardware .

Pour initialiser la structure WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS

Une fois que le pilote a créé l’objet de périphérique, le pilote utilise la fonction WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT pour initialiser la structure. Cette fonction prend deux arguments :

  • Pointeur vers la structure WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS à initialiser.
  • Valeur d’énumération indiquant la prise en charge de la suspension sélective. Le pilote doit spécifier IdleUsbSelectiveSuspend.

Si le pilote spécifie IdleUsbSelectiveSuspend, la fonction initialise les membres de la structure comme suit :

  • IdleTimeout est défini sur IdleTimeoutDefaultValue (actuellement 5 000 millisecondes ou 5 secondes).
  • UserControlOfIdleSettings est réglé sur IdleAllowUserControl.
  • Activé est défini sur WdfUseDefault, ce qui indique que la suspension sélective est activée, mais qu’un utilisateur peut le désactiver si le membre UserControlOfIdleSettings l’autorise.
  • DxState est défini sur PowerDeviceMaximum, qui utilise les fonctionnalités d’alimentation signalées pour l’appareil afin de déterminer l’état vers lequel passer l’appareil inactif.

Pour configurer l’interruption sélective USB

Une fois le pilote initialisé la structure WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS , le pilote peut définir d’autres champs dans la structure, puis appeler WdfDeviceAssignS0IdleSettings pour passer ces paramètres à l’infrastructure. Les champs suivants s’appliquent aux pilotes de fonction USB :

  • IdleTimeout : intervalle, en millisecondes, qui doit s’écouler sans recevoir de requête d’E/S avant que l’infrastructure ne considère l’appareil inactif. Le pilote peut spécifier une valeur ULONG ou accepter la valeur par défaut.

  • UserControlOfIdleSettings : indique si l’utilisateur peut modifier les paramètres inactifs de l’appareil. Les valeurs possibles sont IdleDoNotAllowUserControl et IdleAllowUserControl.

  • DxState : l'état d'alimentation auquel le cadre suspend l'appareil. Les valeurs possibles sont PowerDeviceD1, PowerDeviceD2 et PowerDeviceD3.

    Les pilotes USB ne doivent pas modifier le paramètre initial de cette valeur. La fonction WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT définit cette valeur sur PowerDeviceMaximum, ce qui garantit que l’infrastructure choisit la valeur correcte en fonction des fonctionnalités de l’appareil.

L’extrait de code suivant provient du fichier Device.c de l’exemple de pilote Osrusbfx2 :

WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
NTSTATUS    status = STATUS_SUCCESS;
//
// Initialize the idle policy structure.
//
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, 
     IdleUsbSelectiveSuspend);
idleSettings.IdleTimeout = 10000; // 10 sec

status = WdfDeviceAssignS0IdleSettings(Device, &idleSettings);
if ( !NT_SUCCESS(status)) {
     TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
                 "WdfDeviceSetPowerPolicyS0IdlePolicy failed %x\n", 
                 status);
    return status;
}

Dans l’exemple, le pilote appelle WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT, en spécifiant IdleUsbSelectiveSuspend. Le pilote définit IdleTimeout sur 10 000 millisecondes (10 secondes) et accepte les valeurs par défaut de l’infrastructure pour DxState et UserControlOfIdleSettings. Par conséquent, l’infrastructure passe l’appareil à l’état D3 lorsqu’il est inactif et crée une page de propriétés Device Manager qui permet aux utilisateurs disposant de privilèges d’administrateur d’activer ou de désactiver la prise en charge de l’inactivité de l’appareil. Le pilote appelle ensuite WdfDeviceAssignS0IdleSettings pour activer la prise en charge de l'inactivité et enregistrer ces paramètres dans le cadre.

Un pilote peut appeler WdfDeviceAssignS0IdleSettings à tout moment après avoir créé l'objet de périphérique. Bien que la plupart des pilotes appellent cette méthode initialement à partir du rappel EvtDriverDeviceAdd , cela peut ne pas toujours être possible ou même souhaitable. Si un pilote prend en charge plusieurs appareils ou versions d’appareil, il se peut que le pilote ne connaisse pas toutes les fonctionnalités de l’appareil tant qu’il n’interroge pas le matériel. Ces pilotes peuvent reporter l'appel de WdfDeviceAssignS0IdleSettings jusqu'à l'événement de rappel EvtDevicePrepareHardware.

À tout moment après son appel initial à WdfDeviceAssignS0IdleSettings, le pilote peut modifier la valeur de délai d’inactivité et l’état de l’appareil dans lequel l’appareil est inactif. Pour modifier un ou plusieurs paramètres, le pilote initialise simplement une autre structure WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS comme décrit précédemment et appelle à nouveau WdfDeviceAssignS0IdleSettings .

Prévention de la suspension d’un périphérique USB

Parfois, un périphérique USB ne doit pas être mis hors tension même si aucune demande d’E/S n’est présente dans le délai d’attente, généralement lorsqu’un handle est ouvert à l’appareil ou que l’appareil charge. Un pilote USB peut empêcher l’infrastructure de suspendre un appareil inactif dans de telles situations en appelant WdfDeviceStopIdle et en appelant WdfDeviceResumeIdle lorsqu’il est à nouveau acceptable que l’appareil soit suspendu.

WdfDeviceStopIdle arrête le minuteur d’inactivité. Si la période IdleTimeout n’a pas expiré et que l’appareil n’a pas encore été suspendu, l’infrastructure annule le minuteur d’inactivité et ne suspend pas l’appareil. Si l’appareil a déjà été suspendu, le framework retourne l’appareil à l’état de fonctionnement. WdfDeviceStopIdlen’empêche pas l’infrastructure de suspendre l’appareil lorsque le système passe à un état de veille Sx. Son seul effet est d’empêcher la suspension de l’appareil pendant que le système est dans l’état de fonctionnement S0. WdfDeviceResumeIdle redémarre le minuteur inactif. Ces deux méthodes gèrent un nombre de références sur l’appareil. Par conséquent, si le pilote appelle WdfDeviceStopIdle plusieurs fois, l’infrastructure ne suspend pas l’appareil tant que le pilote n’a pas appelé WdfDeviceResumeIdle le même nombre de fois. Un pilote ne doit pas appeler WdfDeviceResumeIdlesans appeler D’abord WdfDeviceStopIdle.

Inclusion d'une Clé de Registre (Pilotes HID uniquement)

Les pilotes de filtre supérieur KMDF pour les périphériques USB HID doivent indiquer dans l’INF qu’ils prennent en charge la suspension sélective afin que le pilote de port fourni par Microsoft HIDClass.sys puisse activer la suspension sélective pour la pile HID. L’inf doit inclure une directive AddReg qui ajoute la clé SelectiveSuspendEnabled et définit sa valeur sur 1, comme le montre la chaîne suivante :

HKR,,"SelectiveSuspendEnabled",0x00000001,0x1

Pour obtenir un exemple, consultez Hidusbfx2.inx dans wdK à %WinDDK%\BuildNumber\Src\Hid\ Hidusbfx2\sys.

Prise en charge du réveil à distance pour les pilotes KMDF

Comme avec la suspension sélective, KMDF intègre la prise en charge de la mise en éveil, afin qu’un périphérique USB puisse déclencher un signal d'activation pendant que le périphérique est inactif et que le système est en mode actif (S0) ou dans un état de veille (S1-S4). En termes KMDF, ces deux fonctionnalités sont appelées « wake from S0 » et « wake from Sx », respectivement.

Pour les périphériques USB, la mise en éveil indique simplement que l’appareil lui-même peut lancer la transition d’un état d’alimentation inférieur à l’état de travail. Ainsi, en termes USB, le réveil depuis S0 et le réveil depuis Sx sont les mêmes et sont appelés « réveil à distance ».

Les pilotes de fonction USB KMDF n'ont besoin d'aucun code pour permettre le réveil à partir de l'état S0, car KMDF intègre cette capacité dans le mécanisme de suspension sélective. Toutefois, pour prendre en charge le réveil à distance lorsque le système se trouve dans Sx, un pilote de fonction doit :

Les pilotes KMDF configurent généralement la prise en charge de l'activation de réveil en même temps qu’ils configurent la prise en charge de la suspension sélective USB dans la fonction EvtDriverDeviceAdd ou EvtDevicePrepareHardware.

Vérification des fonctionnalités de l’appareil

Avant qu’un pilote de fonction USB KMDF initialise ses paramètres de stratégie d’alimentation pour l’inactivité et le réveil, il doit vérifier que l’appareil prend en charge le réveil distant. Pour obtenir des informations sur les fonctionnalités matérielles de l’appareil, le pilote initialise une structure WDF_USB_DEVICE_INFORMATION et appelle WdfUsbTargetDeviceRetrieveInformation, généralement dans son rappel EvtDriverDeviceAdd ou EvtDevicePrepareHardware .

Dans l’appel à WdfUsbTargetDeviceRetrieveInformation, le pilote transmet un handle vers l'objet de périphérique et un pointeur vers la structure WDF_USB_DEVICE_INFORMATION initialisée. En cas de retour réussi de la fonction, le champ Traits de la structure contient des indicateurs précisant si l’appareil est auto-alimenté, peut fonctionner à grande vitesse et prend en charge le réveil distant.

L’exemple suivant de l’exemple KMDF Osrusbfx2 montre comment appeler cette méthode pour déterminer si un appareil prend en charge le réveil distant. Une fois ces lignes de code exécutées, la variable waitWakeEnable contient TRUE si l’appareil prend en charge la mise en éveil à distance et FALSE si ce n’est pas le cas :

    WDF_USB_DEVICE_INFORMATION          deviceInfo;
// Retrieve USBD version information, port driver capabilities and device
// capabilities such as speed, power, etc.
//

WDF_USB_DEVICE_INFORMATION_INIT(&deviceInfo);

status = WdfUsbTargetDeviceRetrieveInformation(
                            pDeviceContext->UsbDevice,
                            &deviceInfo);
waitWakeEnable = deviceInfo.Traits & WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE;

Activation de la mise en éveil à distance

Dans la terminologie USB, un périphérique USB est activé pour la mise en éveil à distance lorsque sa fonctionnalité de DEVICE_REMOTE_WAKEUP est définie. Selon la spécification USB, le logiciel hôte doit définir la fonctionnalité de mise en éveil à distance sur un appareil « juste avant » pour mettre l’appareil en veille. Le pilote de fonction KMDF est nécessaire uniquement pour initialiser les paramètres de mise en éveil. KMDF et les pilotes de bus USB fournis par Microsoft émettent les demandes d’E/S et gèrent la manipulation matérielle requise pour activer la mise en éveil à distance.

Pour initialiser les paramètres de mise en éveil

  1. Appelez WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT pour initialiser une structure WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS. Cette fonction définit le membre Enabled de la structure sur WdfUseDefault, définit le membre DxState sur PowerDeviceMaximum et définit le membre UserControlOfWakeSettings sur WakeAllowUserControl.
  2. Appelez WdfDeviceAssignSxWakeSettings avec la structure initialisée. Par conséquent, l’appareil est activé pour sortir de l’état D3, et l’utilisateur peut activer ou désactiver le signal de réveil à partir de la page de propriétés de l’appareil dans le Gestionnaire de périphériques.

L’extrait de code suivant de l’exemple Osrusbfx2 montre comment initialiser les paramètres de veille à leurs valeurs par défaut :

WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings;

WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);
status = WdfDeviceAssignSxWakeSettings(Device, &wakeSettings);
if (!NT_SUCCESS(status)) {
    return status;
}

Pour les périphériques USB qui prennent en charge la suspension sélective, le pilote de bus sous-jacent prépare le matériel de l’appareil à se réveiller. Par conséquent, les pilotes de fonction USB ont rarement besoin d'un rappel EvtDeviceArmWakeFromS0. L’infrastructure envoie une demande de suspension sélective au pilote de bus USB lorsque le délai d’inactivité expire.

Pour la même raison, les pilotes de fonction USB nécessitent rarement une routine de rappel EvtDeviceWakeFromS0Triggered ou EvtDeviceWakeFromSxTriggered. Au lieu de cela, le framework et le pilote de bus sous-jacent gèrent toutes les exigences pour ramener l’appareil à un état opérationnel.