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


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

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

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

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

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

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

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

    • Логический адрес буфера (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 из-за ошибки I/O устройства

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

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