Utilisation de Common-Buffer system DMA
Un pilote qui utilise le mode d’initialisation automatique d’un contrôleur DMA système doit allouer de la mémoire pour une mémoire tampon dans laquelle ou à partir de laquelle les transferts DMA peuvent être effectués. Le pilote appelle AllocateCommonBuffer pour obtenir cette mémoire tampon, généralement à partir de la routine DispatchPnP qui gère une requête IRP_MN_START_DEVICE . La figure suivante montre comment un pilote alloue la mémoire tampon et mappe sa plage d’adresses virtuelles à la mémoire physique système.
Comme le montre la figure précédente, un pilote effectue les étapes suivantes pour allouer une mémoire tampon pour le système DMA :
Le pilote appelle AllocateCommonBuffer, en passant un pointeur vers l’objet d’adaptateur retourné par IoGetDmaAdapter, ainsi que la longueur en octets demandée pour sa mémoire tampon. Pour utiliser la mémoire de manière économique, la valeur longueur d’entrée de la mémoire tampon doit être inférieure ou égale à PAGE_SIZE ou être un multiple intégral de PAGE_SIZE.
Si AllocateCommonBuffer retourne un pointeur NULL , le pilote doit libérer toutes les ressources système qu’il a déjà revendiquées et retourner STATUS_INSUFFICIENT_RESOURCES en réponse à la demande IRP_MN_START_DEVICE .
Sinon, AllocateCommonBuffer alloue la quantité de mémoire demandée dans l’espace d’adressage virtuel système et retourne deux types de pointeurs différents vers cette mémoire tampon :
Adresse logique de la mémoire tampon (BufferLogicalAddress dans la figure précédente), pour laquelle le pilote doit fournir un stockage, mais qu’il doit ignorer par la suite
Adresse virtuelle de la mémoire tampon (BufferVirtualAddress dans la figure précédente), que le pilote doit également stocker afin qu’il puisse générer un MDL décrivant sa mémoire tampon pour les opérations DMA
Le pilote doit stocker ces pointeurs dans l’extension de périphérique ou dans une autre mémoire résidente allouée par le pilote.
Le pilote appelle IoAllocateMdl pour allouer un MDL pour la mémoire tampon. Le pilote transmet la VirtualAddress de la mémoire tampon retournée par AllocateCommonBuffer et la longueur de sa mémoire tampon pour allouer une MDL.
Le pilote appelle MmBuildMdlForNonPagedPool avec le pointeur retourné par IoAllocateMdl pour mapper la plage d’adresses virtuelle de sa mémoire tampon résidente à la mémoire physique système.
Après avoir alloué une mémoire tampon commune et mappé sa plage d’adresses virtuelles, le pilote d’un appareil subordonné peut commencer à traiter un IRP qui demande un transfert DMA. Pour ce faire, le pilote appelle la séquence générale suivante de routines de support :
À la discrétion de l’enregistreur de pilotes, RtlMoveMemory pour copier les données d’une mémoire tampon utilisateur verrouillée dans la mémoire tampon commune allouée au pilote pour un transfert vers l’appareil
AllocateAdapterChannel lorsque le pilote est prêt à programmer son appareil pour DMA et a besoin du contrôleur DMA système
MapTransfer, avec le MDL qui décrit la mémoire tampon commune allouée par le pilote, pour configurer le contrôleur DMA système pour l’opération de transfert
Notez que le pilote appelle MapTransfer une seule fois pour configurer le contrôleur DMA système pour utiliser sa mémoire tampon commune. Lors d’un transfert, le pilote peut appeler ReadDmaCounter pour déterminer le nombre d’octets restant à transférer et, si nécessaire, appeler RtlMoveMemory pour copier davantage de données vers ou à partir d’une mémoire tampon utilisateur.
FlushAdapterBuffers lorsque le pilote a terminé son transfert DMA vers/depuis l’appareil subordonné
FreeAdapterChannel dès que toutes les données demandées ont été transférées ou si le pilote doit échouer l’IRP en raison d’une erreur d’E/S d’appareil
Le pointeur d’objet adaptateur retourné par IoGetDmaAdapter est un paramètre obligatoire pour chacune de ces routines de prise en charge, à l’exception de RtlMoveMemory.
Les pilotes individuels appellent cette séquence de routines de support à différents moments, en fonction de la façon dont chaque pilote est implémenté pour traiter son appareil. Par exemple, la routine StartIo d’un pilote peut effectuer l’appel à AllocateAdapterChannel, un autre pilote peut effectuer cet appel à partir d’une routine qui supprime les irps d’une file d’attente verrouillée créée par le pilote, et encore un autre pilote peut effectuer cet appel lorsque son appareil DMA subordonné indique qu’il est prêt à transférer des données.