Partager via


Suspension sélective dans les pilotes de fonction USB KMDF

Cet article décrit comment les pilotes de fonction KMDF prennent en charge la suspension 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 pilotes inférieurs pour suspendre et reprendre l’appareil.

Recommandations pour la suspension sélective dans les pilotes KMDF

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

  • 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 le PPO.
  • Un pilote de fonction KMDF qui prend en charge la suspension sélective peut utiliser des files d’attente 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 les PO sont gérés par 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 le PPO de la pile de périphériques. KMDF gère la suspension et la reprise sélectives pour le compte de ce pilote.

Configuration de la 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 gérées par l’alimentation ou des files d’attente qui ne sont pas gérées par l’alimentation. 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 les demandes de contrôle d’E/S de périphérique 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 requête arrive dans une file d’attente gérée par l’alimentation, KMDF s’assure que l’appareil est dans D0 avant de présenter la demande au pilote.

Si vous écrivez un pilote de filtre KMDF qui est superposé au-dessus du PPO dans la pile des appareils, 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 la suspension de l’appareil, de sorte que l’utilisation de ces files d’attente peut bloquer la pile des appareils.

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

KMDF gère la majeure partie 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 entraînent 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 suit l’activité d’E/S sur toutes les files d’attente gérées par l’alimentation dont chaque objet d’appareil est propriétaire. Le framework démarre un minuteur d’inactivité chaque fois que le nombre d’E/S atteint zéro. La valeur par défaut du délai d’attente est de 5 secondes.

Si une demande d’E/S arrive dans une file d’attente gérée par l’alimentation qui appartient à l’objet d’appareil avant l’expiration du délai d’inactivité, l’infrastructure annule le minuteur d’inactivité et ne suspend pas l’appareil.

Lorsque le minuteur d’inactivité expire, KMDF émet les requêtes nécessaires pour mettre le périphérique USB à 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 n’est pas comptabilisée comme activité du minuteur d’inactivité KMDF. Toutefois, dans la fonction de rappel EvtDeviceD0Exit , le pilote USB doit arrêter manuellement le lecteur continu et toutes les autres cibles d’E/S 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 alors que l’appareil n’est pas en état de fonctionnement. Pour arrêter les cibles, le pilote appelle WdfIoTargetStop et spécifie WdfIoTargetWaitForSentIoToComplete comme action cible. En réponse, l’infrastructure 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 tous les rappels d’achèvement d’E/S associés ont été exécutés.

Par défaut, KMDF fait passer l’appareil hors de D0 et à l’état d’alimentation du périphérique spécifié par le pilote 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 sous tension.

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

Pour reprendre l’appareil, KMDF envoie une demande de mise sous tension vers le bas de la pile de périphériques, 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 rappels impliqués dans les séquences de mise sous 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 la 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’inactivité.
  • Si vous le souhaitez, incluez une logique pour empêcher temporairement la suspension ou la reprise du fonctionnement 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 d’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 et peut ensuite définir des champs qui fournissent des détails sur les fonctionnalités du pilote et de son périphérique. 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, il 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 qui indique 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 défini sur IdleAllowUserControl .
  • Enabled est défini sur WdfUseDefault, ce qui indique que la suspension sélective est activée, mais qu’un utilisateur peut la 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 effectuer la transition de l’appareil inactif.

Pour configurer la suspension sélective USB

Une fois que le pilote a initialisé la structure WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS , il 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 demande d’E/S avant que l’infrastructure considère l’appareil inactif. Le pilote peut spécifier une valeur ULONG ou peut accepter la valeur par défaut.

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

  • DxState : état d’alimentation de l’appareil auquel le framework 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 Gestionnaire de périphériques qui permet aux utilisateurs disposant de privilèges d’administrateur d’activer ou de désactiver la prise en charge des appareils inactifs. Le pilote appelle ensuite WdfDeviceAssignS0IdleSettings pour activer la prise en charge de l’inactivité et inscrire ces paramètres auprès de l’infrastructure.

Un pilote peut appeler WdfDeviceAssignS0IdleSettings à tout moment après avoir créé l’objet d’appareil. 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 de périphérique, il peut ne pas connaître toutes les fonctionnalités de l’appareil tant qu’il n’interroge pas le matériel. Ces pilotes peuvent différer l’appel de WdfDeviceAssignS0IdleSettings jusqu’au rappel EvtDevicePrepareHardware .

À tout moment après son appel initial à WdfDeviceAssignS0IdleSettings, le pilote peut modifier la valeur du 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 de WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS comme décrit précédemment et appelle à nouveau WdfDeviceAssignS0IdleSettings .

Prévention de la suspension du 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’expiration, généralement lorsqu’un handle est ouvert sur l’appareil ou que l’appareil est en cours de chargement. 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, l’infrastructure le retourne à l’état opérationnel. 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 d’inactivité. 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 le périphérique tant que le pilote n’a pas appelé WdfDeviceResumeIdle le même nombre de fois. Un pilote ne doit pas appeler WdfDeviceResumeIdlesans appeler au préalable 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 HIDClass.sys fourni par Microsoft 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 in the WDK at %WinDDK%\BuildNumber\Src\Hid\ Hidusbfx2\sys.

Prise en charge de la sortie de veille à 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 de sortie de veille alors que l’appareil est inactif et que le système est à l’état de fonctionnement (S0) ou en 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érieure à l’état de fonctionnement. Ainsi, en termes USB, wake from S0 et wake from Sx sont les mêmes, et sont appelés « remote wake ».

Les pilotes de fonction USB KMDF n’ont besoin d’aucun code pour prendre en charge l’éveil à partir de S0, car KMDF fournit cette fonctionnalité dans le cadre du mécanisme de suspension sélective. Toutefois, pour prendre en charge la sortie de veille à distance lorsque le système est dans Sx, un pilote de fonction doit :

Les pilotes KMDF configurent généralement la prise en charge du wake 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 l’éveil, il doit vérifier que l’appareil prend en charge la mise en éveil à distance. Pour obtenir des informations sur les fonctionnalités matérielles des appareils, le pilote initialise une structure de 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 à 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 qui indiquent si l’appareil est auto-alimenté, peut fonctionner à grande vitesse et prend en charge la sortie de veille à distance.

L’exemple suivant de l’exemple Osrusbfx2 KMDF montre comment appeler cette méthode pour déterminer si un appareil prend en charge la veille à distance. Une fois ces lignes de code exécutées, la variable waitWakeEnable contient TRUE si l’appareil prend en charge la veille à 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 » la mise en veille de l’appareil. Le pilote de fonction KMDF est nécessaire uniquement pour initialiser les paramètres de sortie de veille. 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 veille

  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 d’éveil à partir de la page de propriétés de l’appareil dans 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 du périphérique à sortir de veille. Ainsi, les pilotes de fonction USB nécessitent rarement un rappel EvtDeviceArmWakeFromS0 . Le framework 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 un rappel EvtDeviceWakeFromS0Triggered ou EvtDeviceWakeFromSxTriggered . Au lieu de cela, l’infrastructure et le pilote de bus sous-jacent gèrent toutes les conditions requises pour le retour de l’appareil à l’état opérationnel.