Verwenden Common-Buffer System-DMA

Ein Treiber, der den Automatischinitialisierungsmodus eines System-DMA-Controllers verwendet, muss Speicher für einen Puffer zuweisen, in den oder von dem DMA-Übertragungen ausgeführt werden können. Der Treiber ruft AllocateCommonBuffer auf, um diesen Puffer abzurufen, in der Regel aus der DispatchPnP-Routine , die eine IRP_MN_START_DEVICE Anforderung verarbeitet. Die folgende Abbildung zeigt, wie ein Treiber den Puffer ordnet und seinen virtuellen Adressbereich dem physischen Systemspeicher zuordnet.

Diagramm, das veranschaulicht, wie ein Treiber einen gemeinsamen Puffer für Systemdma zuordnet.

Wie in der vorherigen Abbildung gezeigt, führt ein Treiber die folgenden Schritte aus, um einen Puffer für System-DMA zuzuweisen:

  1. Der Treiber ruft AllocateCommonBuffer auf und übergibt einen Zeiger auf das Adapterobjekt, das von IoGetDmaAdapter zurückgegeben wurde, zusammen mit der für den Puffer angeforderten Länge in Bytes. Zur wirtschaftlichen Nutzung des Arbeitsspeichers sollte der Eingabelängenwert für den Puffer entweder kleiner oder gleich PAGE_SIZE oder ein integrales Vielfaches von PAGE_SIZE sein.

  2. Wenn AllocateCommonBuffer einen NULL-Zeiger zurückgibt, sollte der Treiber alle bereits beanspruchten Systemressourcen freigeben und STATUS_INSUFFICIENT_RESOURCES als Reaktion auf die IRP_MN_START_DEVICE Anforderung zurückgeben.

    Andernfalls weist AllocateCommonBuffer die angeforderte Arbeitsspeichermenge im virtuellen Adressraum des Systems zu und gibt zwei verschiedene Arten von Zeigern auf diesen Puffer zurück:

    • Die LogicalAddress des Puffers (BufferLogicalAddress in der vorherigen Abbildung), für die der Treiber Speicher bereitstellen muss, die er aber danach ignorieren sollte

    • Die virtuelle Adresse des Puffers (BufferVirtualAddress in der vorherigen Abbildung), die der Treiber ebenfalls speichern muss, damit er eine MDL erstellen kann, die seinen Puffer für DMA-Vorgänge beschreibt.

    Der Treiber sollte diese Zeiger in der Geräteerweiterung oder in einem anderen vom Treiber zugewiesenen residenten Speicher speichern.

  3. Der Treiber ruft IoAllocateMdl auf, um eine MDL für den Puffer zuzuweisen. Der Treiber übergibt die VirtualAddress des von AllocateCommonBuffer zurückgegebenen Puffers und die Länge des Puffers, um eine MDL zuzuweisen.

  4. Der Treiber ruft MmBuildMdlForNonPagedPool mit dem von IoAllocateMdl zurückgegebenen Zeiger auf, um den virtuellen Adressbereich für den residenten Puffer dem physischen Systemspeicher zuzuordnen.

Nach der Zuordnung eines gemeinsamen Puffers und dem Zuordnen des virtuellen Adressbereichs kann der Treiber eines untergeordneten Geräts mit der Verarbeitung eines IRP beginnen, das eine DMA-Übertragung anfordert. Dazu ruft der Treiber die folgende allgemeine Sequenz von Supportroutinen auf:

  1. Nach Ermessen des Treiberschreibers kopiert RtlMoveMemory Daten aus einem gesperrten Benutzerpuffer in den vom Treiber zugewiesenen gemeinsamen Puffer für eine Übertragung auf das Gerät.

  2. AllocateAdapterChannel , wenn der Treiber bereit ist, sein Gerät für DMA zu programmieren und den System-DMA-Controller benötigt

  3. MapTransfer mit der MDL, die den vom Treiber zugewiesenen gemeinsamen Puffer beschreibt, um den System-DMA-Controller für den Übertragungsvorgang einzurichten.

    Beachten Sie, dass der Treiber MapTransfer nur einmal aufruft, um den System-DMA-Controller so einzurichten, dass er seinen gemeinsamen Puffer verwendet. Während einer Übertragung kann der Treiber ReadDmaCounter aufrufen, um zu bestimmen, wie viele Bytes noch übertragen werden sollen, und bei Bedarf RtlMoveMemory aufrufen, um weitere Daten in oder aus einem Benutzerpuffer zu kopieren.

  4. FlushAdapterBuffers , wenn der Treiber die DMA-Übertragung an/vom untergeordneten Gerät abgeschlossen hat

  5. FreeAdapterChannel , sobald alle angeforderten Daten übertragen wurden oder wenn der Treiber die IRP aufgrund eines Geräte-E/A-Fehlers fehlschlagen muss

Der von IoGetDmaAdapter zurückgegebene Adapterobjektzeiger ist ein erforderlicher Parameter für jede dieser Supportroutinen mit Ausnahme von RtlMoveMemory.

Einzelne Treiber rufen diese Sequenz von Supportroutinen an unterschiedlichen Stellen auf, je nachdem, wie die einzelnen Treiber zum Service ihres Geräts implementiert werden. Beispielsweise kann die StartIo-Routine eines Treibers den Aufruf von AllocateAdapterChannel tätigen, ein anderer Treiber diesen Aufruf von einer Routine aus, die IRPs aus einer vom Treiber erstellten, ineinander verriegelten Warteschlange entfernt, und ein anderer Treiber kann diesen Aufruf tätigen, wenn sein untergeordnetes DMA-Gerät angibt, dass es bereit ist, Daten zu übertragen.