Поделиться через


Использование DMA системы Common-Buffer

Драйвер, использующий режим автоматической инициализации контроллера DMA системы, должен выделить память для буфера, в который или из которого можно осуществлять передачу данных DMA. Драйвер вызывает AllocateCommonBuffer , чтобы получить этот буфер, как правило, из подпрограммы DispatchPnP , которая обрабатывает запрос IRP_MN_START_DEVICE . На следующем рисунке показано, как драйвер выделяет буфер и сопоставляет его диапазон виртуальных адресов с физической памятью системы.

схема, иллюстрирующая, как драйвер выделяет общий буфер для системного dma.

Как показано на предыдущем рисунке, драйвер выполняет следующие действия, чтобы выделить буфер для системного DMA:

  1. Драйвер вызывает AllocateCommonBuffer, передав указатель на объект адаптера, возвращенный IoGetDmaAdapter, вместе с длиной в байтах, запрошенной для его буфера. Для экономичного использования памяти входное значение Length для буфера должно быть меньше или равно PAGE_SIZE или должно быть целочисленным, кратным PAGE_SIZE.

  2. Если Функция AllocateCommonBuffer возвращает указатель NULL , драйвер должен освободить все уже запрошенные системные ресурсы и вернуть STATUS_INSUFFICIENT_RESOURCES в ответ на запрос IRP_MN_START_DEVICE .

    В противном случае AllocateCommonBuffer выделяет запрошенный объем памяти в системном виртуальном адресном пространстве и возвращает два разных типа указателей на этот буфер:

    • LogicalAddress буфера (BufferLogicalAddress на предыдущем рисунке), для которого драйвер должен предоставить хранилище, но который он должен игнорировать после этого.

    • Виртуальный адрес буфера (BufferVirtualAddress на предыдущем рисунке), который драйвер также должен сохранить, чтобы он смог создать MDL, описывающий его буфер для операций DMA.

    Драйвер должен хранить эти указатели в расширении устройства или другой выделенной драйвером резидентной памяти.

  3. Драйвер вызывает IoAllocateMdl , чтобы выделить MDL для буфера. Драйвер передает значение VirtualAddress буфера , возвращаемого параметром AllocateCommonBuffer , и значение Длины буфера для выделения MDL.

  4. Драйвер вызывает MmBuildMdlForNonPagedPool с указателем, возвращенным IoAllocateMdl , чтобы сопоставить диапазон виртуальных адресов для его постоянного буфера с системной физической памятью.

После выделения общего буфера и сопоставления его виртуального диапазона адресов драйвер подчиненного устройства может начать обработку IRP, запрашивающего передачу DMA. Для этого драйвер вызывает следующую общую последовательность процедур поддержки:

  1. По усмотрению модуля записи драйвера RtlMoveMemory копирует данные из заблокированного пользовательского буфера в общий буфер, выделенный драйвером для передачи на устройство.

  2. AllocateAdapterChannel , когда драйвер готов к программированию своего устройства для DMA и нуждается в системном контроллере DMA

  3. MapTransfer с MDL, описывающим общий буфер, выделенный драйвером, для настройки системного контроллера DMA для операции передачи

    Обратите внимание, что драйвер вызывает MapTransfer только один раз, чтобы настроить системный контроллер DMA для использования общего буфера. Во время передачи драйвер может вызвать ReadDmaCounter , чтобы определить, сколько байтов осталось передать, и при необходимости вызвать RtlMoveMemory , чтобы скопировать дополнительные данные в буфер пользователя или из нее.

  4. FlushAdapterBuffers, когда драйвер завершил передачу DMA на подчиненное устройство или с него

  5. FreeAdapterChannel сразу после передачи всех запрошенных данных или если драйвер должен завершиться сбоем IRP из-за ошибки ввода-вывода устройства.

Указатель на объект адаптера, возвращаемый IoGetDmaAdapter , является обязательным параметром для каждой из этих подпрограмм поддержки, за исключением RtlMoveMemory.

Отдельные драйверы называют эту последовательность подпрограмм поддержки в разных точках в зависимости от того, как каждый драйвер реализуется для обслуживания своего устройства. Например, подпрограмма StartIo одного драйвера может выполнить вызов AllocateAdapterChannel, другой драйвер может сделать этот вызов из подпрограммы, которая удаляет IRP из созданной драйвером заблокированной очереди, а другой драйвер может выполнить этот вызов, когда его подчиненное устройство DMA указывает, что готово к передаче данных.