Grundlegendes Aufrufmuster für DMA-Routinen der Version 3
Um eine DMA-Übertragung durchzuführen, die die Routinen in Version 3 der DMA-Betriebsschnittstelle verwendet, sollte Ihr Treiber die in der folgenden Liste beschriebenen Schritte ausführen. Diese Schritte gelten sowohl für untergeordnete Geräte als auch für Bus-master-Geräte. Version 3 dieser Schnittstelle ist ab Windows 8 verfügbar. Weitere Informationen zu den Routinen in dieser Schnittstelle finden Sie unter DMA_OPERATIONS.
Schritt 1: Abrufen eines DMA-Adapterobjekts
Zur Vorbereitung einer DMA-Übertragung ruft der Treiber die IoGetDmaAdapter-Routine auf, um ein DMA-Adapterobjekt zu erhalten. Ein DMA-Adapterobjekt ist ein Softwareobjekt, das entweder ein Bus-master-Gerät oder eine Anforderungszeile auf einem DMA-Systemcontroller darstellt. Dieses Objekt enthält die DMA-Betriebsschnittstelle für den Bus, der zum Übertragen von Daten zum oder vom Gerät verwendet wird. Darüber hinaus synchronisiert dieses Objekt den Zugriff des Treibers auf die freigegebenen Ressourcen, die für die Übertragung erforderlich sind. Weitere Informationen finden Sie unter Einführung in Adapterobjekte.
Schritt 2: Abrufen einer Beschreibung der erforderlichen DMA-Ressourcen
Der Treiber ruft die GetDmaTransferInfo-Routine auf, um eine Beschreibung der DMA-Ressourcen abzurufen, die er für die Übertragung benötigt.
Die Eingabeparameter für diesen Aufruf beschreiben den Speicherpuffer, der für die Übertragung verwendet werden soll, und die Richtung (Lese- oder Schreibzugriff) der Übertragung.
Die Ressourcenanforderungen, die aus diesem Aufruf abgerufen werden, umfassen die Anzahl der Kartenregister und die Größe der Punkt-/Sammlungsliste, die zum Beschreiben des Datenpuffers für die Übertragung erforderlich ist. Im nachfolgenden Aufruf der AllocateAdapterChannelEx-Routine (siehe Schritt 3) stellt der Treiber die Kartenregisteranzahl als Eingabeparameter bereit.
Schritt 3: Anfordern der erforderlichen DMA-Ressourcen
Der Treiber ruft die AllocateAdapterChannelEx-Routine auf, um Ressourcen zuzuweisen, die dem DMA-Adapterobjekt zugewiesen werden sollen. Zu diesen Ressourcen gehören ein DMA-Kanal und Zuordnungsregister.
Ein AllocateAdapterChannelEx-Aufruf kann asynchron oder synchron sein.
Wenn das flag DMA_SYNCHRONOUS_CALLBACK nicht festgelegt ist, erfolgt der Aufruf asynchron. In diesem Fall verweist der Parameter ExecutionRoutine auf eine vom Aufrufer bereitgestellte Ausführungsroutine, die aufgerufen wird, wenn die angeforderten Ressourcen verfügbar sind. Bei erfolgreicher Ausführung gibt ein asynchroner AllocateAdapterChannelEx-Aufruf STATUS_SUCCESS zurück, ohne auf die Ausführungsroutine zu warten.
Wenn das flag DMA_SYNCHRONOUS_CALLBACK festgelegt ist, ist der AllocateAdapterChannelEx-Aufruf synchron. In diesem Fall ist der ExecutionRoutine-Parameter im Aufruf optional, und AllocateAdapterChannelEx verhält sich wie folgt:
Wenn ExecutionRoutine nicht NULL ist und die DMA-Ressourcen sofort zugeordnet werden können, ruft AllocateAdapterChannelEx die Ausführungsroutine im Kontext des aufrufenden Threads auf. Nachdem die Ausführung der Ausführungsroutine abgeschlossen ist, gibt AllocateAdapterChannelEx STATUS_SUCCESS zurück. Wenn die Ressourcen nicht sofort verfügbar sind, schlägt AllocateAdapterChannelEx fehl und gibt den Fehler status Code STATUS_INSUFFICIENT_RESOURCES zurück.
Wenn ExecutionRoutine NULL ist und AllocateAdapterChannelEx die DMA-Ressourcen sofort zuordnen kann, gibt AllocateAdapterChannelEx STATUS_SUCCESS zurück. Wenn nicht alle Ressourcen sofort verfügbar sind, schlägt der Aufruf mit einem Fehler status Code STATUS_INSUFFICIENT_RESOURCES fehl.
Wenn der MapRegisterBase-Parameter für AllocateAdapterChannelEx bei synchronen Aufrufen, die STATUS_SUCCESS zurückgeben, nicht NULL ist, schreibt AllocateAdapterChannelEx die Basisadresse der zugeordneten Zuordnungsregister in die Adresse, auf die der MapRegisterBase-Parameter verweist. Wenn ExecutionRoutine NULL ist, muss MapRegisterBase nicht NULL sein. Wenn ExecutionRoutine nicht NULL ist, ist der MapRegisterBase-Parameter für AllocateAdapterChannelEx optional, und die Ausführungsroutine empfängt die Basisadresse des Zuordnungsregisters als Eingabeparameter.
Für asynchrone AllocateAdapterChannelEx-Aufrufe muss ExecutionRoutine nicht NULL sein, und die Ausführungsroutine empfängt die Basisadresse des Zuordnungsregisters als Eingabeparameter.
Bei nachfolgenden Aufrufen der MapTransferEx-Routine (siehe Schritt 5) stellt der Treiber die Basisadresse des Kartenregisters als Eingabeparameter bereit.
Wenn ExecutionRoutine ungleich NULL ist, gibt die Ausführungsroutine einen status Wert zurück, der die Disposition der zugeordneten Ressourcen angibt. Bei DMA-Systemübertragungen muss dieser Rückgabewert KeepObject sein. Dieser Wert informiert das Betriebssystem darüber, dass das Adapterobjekt (und alle zugeordneten Ressourcen) verwendet wird und nicht freigegeben werden soll. Wenn keine Ausführungsroutine angegeben wird, muss der Treiber stattdessen die FreeAdapterObject-Routine aufrufen und KeepObject als AllocationOption-Parameter angeben.
Schritt 4: Abbrechen der ausstehenden Ressourcenanforderung bei Bedarf
Nachdem ein AllocateAdapterChannelEx-Aufruf einen DMA-Adapter in die Warteschlange gestellt hat, um auf DMA-Ressourcen zu warten, kann der Treiber bei Bedarf die CancelAdapterChannel-Routine aufrufen, um die ausstehende Ressourcenanforderung abzubrechen.
Wenn CancelAdapterChannel TRUE zurückgibt, wird die Ressourcenanforderung erfolgreich abgebrochen. Wenn im AllocateAdapterChannelEx-Aufruf eine Ausführungsroutine bereitgestellt wurde, wird diese Routine nicht ausgeführt.
Wenn CancelAdapterChannel FALSE zurückgibt, kann die Ressourcenanforderung nicht abgebrochen werden, da sie bereits erteilt wurde. Wenn im AllocateAdapterChannelEx-Aufruf eine Ausführungsroutine bereitgestellt wurde, wird diese Routine aufgerufen.
Schritt 5: Initialisieren der DMA-Ressourcen und Starten der DMA-Übertragung
Der Treiber ruft MapTransferEx auf, um die DMA-Ressourcen zu initialisieren und die DMA-Übertragung zu starten. Dieser Aufruf kann im selben Treiberthread erfolgen, der AllocateAdapterChannelEx aufruft, oder er kann in der Ausführungsroutine auftreten, die der Treiber an AllocateAdapterChannelEx bereitstellt. Wenn mehrere MapTransferEx-Aufrufe erforderlich sind, um den gesamten DMA-Datenpuffer zu übertragen, kann in der Abschlussroutine für den vorherigen MapTransferEx-Aufruf ein späterer MapTransferEx-Aufruf erfolgen.
MapTransferEx unterstützt verkettete MDLs als Eingabeparameter. Jede MDL beschreibt einen Bereich des DMA-Puffers, der im virtuellen Arbeitsspeicher zusammenhängend ist. Wenn MapTransferEx die Punkt-/Gather-Liste erstellt, werden die Übergänge von einem praktisch zusammenhängenden Pufferbereich zum nächsten automatisch ohne Treibereingriff verarbeitet. Weitere Informationen finden Sie unter Verwenden der MapTransferEx-Routine.
Bei einer DMA-Systemübertragung kann im optionalen DmaCompletionRoutine-Parameter ein Zeiger auf eine DMA-Vervollständigungsroutine an MapTransferEx übergeben werden. Diese Routine wird als Reaktion auf einen Interrupt des DMA-Systems auf Dispatchebene ausgeführt, der angibt, dass die DMA-Übertragung abgeschlossen ist.
Wenn MapTransferEx nicht die gesamte angeforderte Übertragungsgröße zuordnen kann, wird der *Length-Ausgabeparameter auf die zugeordnete Länge festgelegt und STATUS_SUCCESS zurückgegeben.
Schritt 6: Führen Sie bei Bedarf hardwarespezifische Vorgänge aus.
MapTransferEx gibt STATUS_SUCCESS zurück, um anzugeben, dass die DMA-Übertragung erfolgreich initiiert wurde. Auf einigen Plattformen muss der Treiber möglicherweise zusätzliche Maßnahmen außerhalb des MapTransferEx-Aufrufs ergreifen, um die Übertragung zu starten, aber diese Art von verzögertem Start ist nicht für alle Plattformen erforderlich. Die Fahrer dürfen bei Entscheidungen über die Verwendung und Freigabe der zugeordneten Ressourcen nicht auf solche Verzögerungen angewiesen sein.
Die Routinen in der DMA-Betriebsschnittstelle behalten die Cachekohärenz für DMA-Übertragungen auf eine Weise bei, die für die Treiber, die diese Routinen verwenden, transparent ist. Auf Plattformen, die keine Cachekohärenz in Hardware erzwingen, stellt MapTransferEx sicher, dass Prozessordatencaches vor der Übertragung von Schreibvorgängen (Arbeitsspeicher zu Gerät) geleert werden. Bei Leseübertragungen (Device-to-Memory) werden die Caches während des Aufrufs der FlushAdapterBuffersEx-Routine ungültig (siehe Schritt 8), die auf jeden MapTransferEx-Aufruf folgt.
Schritt 7: Empfangen einer Benachrichtigung, wenn die DMA-Übertragung abgeschlossen ist
Wenn eine DMA-Übertragung abgeschlossen ist, wird der Treiber auf eine der folgenden beiden Arten benachrichtigt:
- Eine Unterbrechung des Gerätetreibers für ein Bus-master-Gerät
- Ausführung der vom Treiber bereitgestellten Vervollständigungsroutine für ein untergeordnetes Gerät, das einen DMA-Systemcontroller verwendet
Für eine DMA-Systemübertragung kann ein Treiber eine Vervollständigungsroutine für MapTransferEx als Eingabeparameter bereitstellen.
Schritt 8: Leeren aller daten, die im Cache verbleiben
Nach Abschluss der DMA-Übertragung muss der Treiber die FlushAdapterBuffersEx-Routine aufrufen, um alle Daten zu leeren, die im Cache verbleiben. Der Treiber muss FlushAdapterBuffersEx nach jedem MapTransferEx-Aufruf aufrufen.
Wenn ein MapTransferEx-Aufruf nur einen Teil des DMA-Datenpuffers zuordnet, muss der Treiber MapTransferEx erneut aufrufen, um die verbleibenden Daten zuzuordnen. Eine komplexe Übertragung erfordert möglicherweise mehrere MapTransferEx-Aufrufe . Wiederholen Sie für jeden weiteren MapTransferEx-Aufruf die Schritte 5 bis 8.
Schritt 9: Freigeben von DMA-Kanal- und Kartenregistern
Nachdem der gesamte DMA-Datenpuffer erfolgreich zugeordnet wurde und die endgültige Übertragung abgeschlossen wurde, muss der Treiber die FreeAdapterChannel-Routine aufrufen, um den DMA-Kanal und alle zuvor zugeordneten Kartenregister freizusetzen.
Schritt 10: Freigeben des DMA-Adapterobjekts
Nachdem alle DMA-Übertragungen abgeschlossen sind und alle zuvor zugeordneten Kartenregister freigegeben wurden, ruft der Treiber die PutDmaAdapter-Routine auf, um das Adapterobjekt freizugeben.