StartIo Routines dans les pilotes Lowest-Level
L’appel du gestionnaire d’E/S à la routine de répartition d’un pilote est la première étape de la satisfaction d’une demande d’E/S d’appareil. La routine StartIo est la deuxième étape. Chaque pilote de périphérique avec une routine StartIo est susceptible d’appeler IoStartPacket à partir de ses routines DispatchRead et DispatchWrite , et généralement pour un sous-ensemble des codes de contrôle d’E/S qu’il prend en charge dans sa routine DispatchDeviceControl . La routine IoStartPacket ajoute l’IRP à la file d’attente d’appareils fournie par le système de l’appareil ou, si la file d’attente est vide, appelle immédiatement la routine StartIo du pilote pour traiter l’IRP.
Vous pouvez supposer que lorsque la routine StartIo d’un pilote est appelée, l’appareil cible n’est pas occupé. Cela est dû au fait que le gestionnaire d’E/S appelle StartIo dans deux circonstances ; soit l’une des routines de distribution du pilote vient d’appeler IoStartPacket et la file d’attente de l’appareil était vide, soit la routine DpcForIsr du pilote effectue une autre demande et vient d’appeler IoStartNextPacket pour mettre en file d’attente le prochain IRP.
Avant l’appel de la routine StartIo dans un pilote de périphérique de niveau supérieur, cette routine de répartition du pilote doit avoir sondé et verrouillé la mémoire tampon utilisateur, si nécessaire, pour configurer des adresses de mémoire tampon mappées valides dans l’IRP mis en file d’attente vers sa routine StartIo . Si un pilote de périphérique de niveau supérieur configure ses objets d’appareil pour les E/S directes (ou pour les E/S directes ou mises en mémoire tampon), le pilote ne peut pas différer le verrouillage d’une mémoire tampon utilisateur sur sa routine StartIo ; chaque routine StartIo est appelée dans un contexte de thread arbitraire à l’adresse IRQL = DISPATCH_LEVEL.
Notes
Toute mémoire tampon accessible par la routine StartIo d’un pilote doit être verrouillée ou allouée à partir de la mémoire résidente de l’espace système et doit être accessible dans un contexte de thread arbitraire.
En général, la routine StartIo de tout pilote de périphérique de niveau inférieur est chargée d’appeler IoGetCurrentIrpStackLocation avec l’IRP d’entrée, puis d’effectuer le traitement spécifique à la demande nécessaire pour démarrer l’opération d’E/S sur son appareil. Le traitement spécifique à la demande peut inclure les éléments suivants :
Configuration ou mise à jour des informations d’état sur la demande actuelle que le pilote gère. Les informations d’état peuvent être stockées dans l’extension de l’appareil de l’objet d’appareil cible ou ailleurs dans un pool non paginé alloué par le pilote.
Par exemple, si un pilote de périphérique conserve une valeur booléenne InterruptExpected pour l’opération de transfert en cours, sa routine StartIo peut définir cette variable sur TRUE. Si le pilote conserve un compteur de délai d’attente pour l’opération en cours, sa routine StartIo peut configurer cette valeur ou la routine StartIo peut mettre en file d’attente la routine CustomTimerDpc du pilote.
Si la routine StartIo partage l’accès aux informations d’état ou aux ressources matérielles avec d’autres routines de pilotes, les informations d’état ou la ressource doivent être protégées par un verrou de rotation. (Voir Spin Locks.)
Si la routine StartIo partage l’accès aux informations d’état ou aux ressources avec la routine InterruptService du pilote, StartIo doit utiliser KeSynchronizeExecution pour appeler une routine SynchCritSection qui accède aux informations d’état ou de ressource. (Consultez Utilisation des sections critiques.)
Affectation d’un numéro de séquence à l’IRP au cas où le pilote doit consigner une erreur d’E/S d’appareil lors du traitement de l’IRP.
Pour plus d’informations, consultez Erreurs de journalisation .
Si nécessaire, traduisez les paramètres dans l’emplacement de la pile d’E/S du pilote en valeurs spécifiques à l’appareil.
Par exemple, un pilote de disque peut avoir besoin de calculer le décalage entre le secteur de départ ou l’octet sur l’adresse de disque physique pour une opération de transfert, et déterminer si la longueur demandée du transfert dépasse une limite de secteur particulière ou dépasse la capacité de transfert de son périphérique physique.
Si le pilote contrôle un périphérique multimédia amovible, vérifiez s’il y a des modifications de média avant de programmer l’appareil pour les E/S et d’informer son système de fichiers trop haut si le média a changé.
Pour plus d’informations, consultez Prise en charge du support amovible.
Si l’appareil utilise DMA, vérifiez si la longueur demandée (nombre d’octets à transférer, trouvé dans l’emplacement de la pile d’E/S du pilote de l’IRP) doit être fractionnée en opérations de transfert partiel, comme expliqué dans Techniques d’entrée/sortie, en supposant qu’un pilote de niveau supérieur étroitement couplé ne présplitait pas de grands transferts pour le pilote de périphérique.
La routine StartIo d’un tel pilote de périphérique peut également être chargée d’appeler KeFlushIoBuffers et, si le pilote utilise la DMA basée sur les paquets, d’appeler AllocateAdapterChannel avec la routine AdapterControl du pilote.
Pour plus d’informations, consultez Objets d’adaptateur et DMA et Maintenance de la cohérence du cache.
Si l’appareil utilise PIO, mappez l’adresse virtuelle de base de la mémoire tampon, décrite dans l’IRP à irp-MdlAddress>, à une adresse d’espace système avec MmGetSystemAddressForMdlSafe.
Pour les demandes de lecture, la routine StartIo du pilote de périphérique peut être chargée d’appeler KeFlushIoBuffers avant le début des opérations PIO. Pour plus d’informations, consultez Maintenance de la cohérence du cache .
Si un pilote non WDM utilise un objet contrôleur, appelez IoAllocateController pour inscrire sa routine ControllerControl .
Si le pilote gère les IRP annulables, vérifiez si l’IRP d’entrée a déjà été annulé.
Si un IRP d’entrée peut être annulé avant d’être traité jusqu’à l’achèvement, la routine StartIo doit appeler IoSetCancelRoutine avec l’IRP et le point d’entrée de la routine Cancel du pilote. La routine StartIo doit acquérir le verrou d’annulation de rotation pour son appel à IoSetCancelRoutine. Un pilote peut également utiliser IoSetStartIoAttributes pour définir l’attribut NonCancelable de la routine StartIo sur TRUE. Cela empêche le système d’essayer d’annuler un IRP qui a été passé à StartIo par un appel à IoStartPacket.
En règle générale, un pilote qui utilise des E/S mises en mémoire tampon a une routine StartIo plus simple que celui qui utilise des E/S directes. Les pilotes qui utilisent des E/S mises en mémoire tampon transfèrent de petites quantités de données pour chaque demande de transfert, tandis que ceux qui utilisent des E/S directes (qu’il s’agisse d’E/S DMA ou PIO) transfèrent de grandes quantités de données vers ou depuis des mémoires tampons verrouillées qui peuvent couvrir les limites de page physiques dans la mémoire système.
Les pilotes de niveau supérieur superposés aux pilotes de périphériques physiques configurent généralement leurs objets d’appareil pour qu’ils correspondent à ceux de leurs pilotes de périphérique respectifs. Toutefois, un pilote de niveau supérieur, en particulier un pilote de système de fichiers, peut configurer des objets d’appareil pour les E/S directes ou mises en mémoire tampon.
Les pilotes qui configurent leurs objets d’appareil pour les E/S mises en mémoire tampon peuvent s’appuyer sur le gestionnaire d’E/S pour passer des mémoires tampons valides dans tous les IRP qu’il envoie au pilote. Les pilotes de niveau inférieur qui configurent des objets d’appareil pour les E/S directes peuvent s’appuyer sur le pilote de niveau le plus élevé de leur chaîne pour passer des mémoires tampons valides dans tous les IRPs envoyés via tous les pilotes intermédiaires au pilote de périphérique de niveau inférieur sous-jacent.
Utilisation des E/S mises en mémoire tampon dans les routines StartIo
Si la routine DispatchRead, DispatchWrite ou DispatchDeviceControl d’un pilote détermine qu’une demande est valide et appelle IoStartPacket, le gestionnaire d’E/S appelle la routine StartIo du pilote pour traiter immédiatement l’IRP si la file d’attente des appareils est vide. Si la file d’attente n’est pas vide, IoStartPacket met en file d’attente l’IRP. Finalement, un appel à IoStartNextPacket à partir de la routine DpcForIsr ou CustomDpc du pilote entraîne le gestionnaire d’E/S à retirer l’IRP et à appeler la routine StartIo du pilote.
La routine StartIo appelle IoGetCurrentIrpStackLocation et détermine quelle opération doit être effectuée pour répondre à la demande. Il prétraite l’IRP de toute manière nécessaire avant de programmer l’appareil physique pour effectuer la demande d’E/S.
Si l’accès à l’appareil physique (ou à l’extension de l’appareil) doit être synchronisé avec une routine InterruptService , la routine StartIo doit appeler une routine SynchCritSection pour effectuer la programmation d’appareil nécessaire. Pour plus d’informations, consultez Utilisation de sections critiques.
Un pilote de périphérique physique qui utilise des E/S mises en mémoire tampon transfère des données vers ou à partir d’une mémoire tampon d’espace système, allouée par le gestionnaire d’E/S, que le pilote trouve dans chaque IRP sur Irp-AssociatedIrp.SystemBuffer>.
Utilisation d’E/S directes dans les routines StartIo
Si la routine DispatchRead, DispatchWrite ou DispatchDeviceControl d’un pilote détermine qu’une demande est valide et appelle IoStartPacket, le gestionnaire d’E/S appelle la routine StartIo du pilote pour traiter immédiatement l’IRP si la file d’attente des appareils est vide. Si la file d’attente n’est pas vide, IoStartPacket met en file d’attente l’IRP. Finalement, un appel à IoStartNextPacket à partir de la routine DpcForIsr ou CustomDpc du pilote entraîne le gestionnaire d’E/S à retirer l’IRP et à appeler la routine StartIo du pilote.
La routine StartIo appelle IoGetCurrentIrpStackLocation et détermine quelle opération doit être effectuée pour répondre à la demande. Il prétraite l’IRP de toutes les manières nécessaires, comme le fractionnement d’une demande de transfert DMA volumineuse en plages de transfert partielles et l’enregistrement de l’état concernant la longueur d’une demande de transfert entrante qui doit être fractionnée. Ensuite, il programme l’appareil physique pour effectuer la demande d’E/S.
Si l’accès à l’appareil physique (ou à l’extension de l’appareil) doit être synchronisé avec l’ISR du pilote, la routine StartIo doit utiliser une routine SynchCritSection fournie par le pilote pour effectuer la programmation nécessaire. Pour plus d’informations, consultez Utilisation de sections critiques.
Tout pilote qui utilise des E/S directes lit des données dans ou écrit des données à partir d’une mémoire tampon verrouillée, décrite par une liste de descripteurs de mémoire (MDL), que le pilote trouve dans l’IRP sur Irp-MdlAddress>. Un tel pilote utilise généralement des E/S mises en mémoire tampon pour les demandes de contrôle d’appareil. Pour plus d’informations, consultez Gestion des demandes de contrôle d’E/S dans les routines StartIo.
Le type MDL est un type opaque auquel les pilotes n’accèdent pas directement. Au lieu de cela, les pilotes qui utilisent PIO remapment les mémoires tampons d’espace utilisateur en appelant MmGetSystemAddressForMdlSafe avec Irp-MdlAddress> comme paramètre. Les pilotes qui utilisent DMA passent également Irp-MdlAddress> pour prendre en charge les routines pendant leurs opérations de transfert afin que les adresses de mémoire tampon aient été réappées dans des plages logiques pour leurs appareils.
À moins qu’un pilote de niveau supérieur étroitement couplé fractionne les demandes de transfert DMA volumineuses pour le pilote de périphérique sous-jacent, la routine StartIo d’un pilote de périphérique de niveau inférieur doit fractionner chaque demande de transfert supérieure à ce que son appareil peut gérer en une seule opération de transfert. Les pilotes qui utilisent la DMA système sont nécessaires pour fractionner les demandes de transfert qui sont trop volumineuses pour le contrôleur DMA système ou pour que leurs appareils soient gérés en une seule opération de transfert.
Si l’appareil est un périphérique DMA subordonné, son pilote doit synchroniser les transferts via un contrôleur DMA système avec un objet d’adaptateur alloué au pilote, représentant le canal DMA et une routine AdapterControl fournie par le pilote. Le pilote d’un périphérique DMA master bus doit également utiliser un objet d’adaptateur alloué par le pilote pour synchroniser ses transferts et doit fournir une routine AdapterControl s’il utilise la prise en charge DMA basée sur les paquets du système, ou une routine AdapterListControl s’il utilise la prise en charge de la diffusion/collecte du système.
Selon la conception du pilote, il peut synchroniser les opérations de transfert et de contrôle d’appareil sur un appareil physique avec un objet contrôleur et fournir une routine ControllerControl .
Pour plus d’informations, consultez Adapter Objects et DMA and Controller Objects .
Gestion des demandes de contrôle d’E/S dans les routines StartIo
En général, seul un sous-ensemble des demandes de contrôle d’E/S d’appareil est transmis à partir de la routine DispatchDeviceControl ou DispatchInternalDeviceControl d’un pilote pour un traitement ultérieur par la routine StartIo du pilote. La routine StartIo du pilote doit uniquement gérer les demandes de contrôle d’appareil valides qui nécessitent des changements d’état de l’appareil ou renvoyer des informations volatiles sur l’état actuel de l’appareil.
Chaque nouveau pilote doit prendre en charge le même ensemble de codes de contrôle d’E/S publics que tous les autres pilotes pour le même type d’appareil. Le système définit des codes de contrôle d’E/S publics spécifiques au type d’appareil pour les requêtes IRP_MJ_DEVICE_CONTROL en tant que requêtes mises en mémoire tampon.
Par conséquent, les pilotes de périphériques physiques effectuent des transferts de données vers ou à partir d’une mémoire tampon d’espace système que chaque pilote trouve dans l’IRP sur Irp-AssociatedIrp.SystemBuffer> pour les demandes de contrôle d’appareil. Même les pilotes qui configurent leurs objets d’appareil pour les E/S directes utilisent des E/S mises en mémoire tampon pour satisfaire les demandes de contrôle d’appareil avec des codes de contrôle d’E/S publics.
La définition de chaque code de contrôle d’E/S détermine si les données transférées pour cette demande sont mises en mémoire tampon. Tous les codes de contrôle d’E/S définis en privé pour les demandes d’IRP_MJ_INTERNAL_DEVICE_CONTROL spécifiques aux pilotes entre des pilotes jumelés peuvent définir un code avec une méthode mise en mémoire tampon, une méthode directe ou une méthode ni l’une ni l’autre. En règle générale, tout code de contrôle d’E/S défini en privé ne doit pas être défini avec la méthode ni si un pilote de niveau supérieur étroitement couplé doit allouer une mémoire tampon pour cette demande.
Programmation de l’appareil pour les opérations d’E/S
En règle générale, la routine StartIo dans un pilote de périphérique de niveau le plus bas doit synchroniser l’accès à la mémoire ou aux inscriptions d’appareil qu’elle partage avec l’ISR du pilote à l’aide de KeSynchronizeExecution pour appeler une routine SynchCritSection fournie par le pilote. La routine StartIo du pilote utilise la routine SynchCritSection pour programmer l’appareil physique pour les E/S au niveau de DIRQL. Pour plus d’informations, consultez Utilisation de sections critiques.
Avant d’appeler KeSynchronizeExecution, la routine StartIo doit effectuer le prétraitement nécessaire pour la requête. Le prétraitement peut inclure le calcul d’une plage de transfert partiel initiale et l’enregistrement des informations d’état relatives à la demande d’origine pour d’autres routines de pilotes.
Si un pilote de périphérique utilise DMA, sa routine StartIo appelle généralement AllocateAdapterChannel avec une routine AdapterControl fournie par le pilote. Dans ces circonstances, la routine StartIo reporte la responsabilité de la programmation de l’appareil physique à la routine AdapterControl . À son tour, il peut appeler KeSynchronizeExecution pour avoir un programme de routine SynchCritSection fourni par le pilote pour un transfert DMA.