Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

На главную -> MyLDP -> Электронные книги по ОС Linux
Цилюрик О.И. Модули ядра Linux
Назад Обслуживание периферийных устройств Вперед

DMA

Работа PCI устройства может быть предусмотрена как по прямому чтению адресов ввода/вывода, так и (что гораздо чаще) пользуясь механизмом DMA (Direct Memory Access). Передача данных по DMA организуется на аппаратном уровне, и выполняется (например, когда программа запрашивает данные через такую функцию, например, как read()) в таком порядке:

- когда процесс вызывает read(), метод драйвера выделяет буфер DMA (или указывает адрес в ранее выделенном буфере) и выдаёт команду оборудованию передавать свои данные в этот буфер (указывая в этой команде адрес начала передачи и объём передачи); процесс после этого блокируется;

- периферийное устройство аппаратно захватывает шину обмена и записывает данные последовательно в буфер DMA с указанного адреса, после этого вызывает прерывание, когда весь заказанный объём передан;

- обработчик прерывания получает входные данные, подтверждает прерывание и переводит процесс в активное состояние, процесс теперь имеет возможность читать данные.

Установленные в системе каналы обмена по DMA отображаются в файловую систему /proc:

$ cat /proc/dma

	 2: floppy 
	 4: cascade 

Организация обмена по DMA это основной способ взаимодействия со всеми высокопроизводительными устройствами. С другой стороны, обмен по DMA полностью зависим от деталей аппаратной реализации, поэтому в общем виде может быть рассмотрен только достаточно поверхностно. Буфера DMA могут выделяться только в строго определённых областях памяти:

- эта память должна распределяться в физически непрерывной области памяти, выделение посредством vmalloc() неприменимо, память под буфера должна выделяться kmalloc() или __get_free_pages();

- для многих архитектур выделение памяти должно быть специфицировано с флагом GFP_DMA, для x86 это будет выделение ниже адреса MAX_DMA_ADDRESS=16MB;

- память должна выделяться начиная с границы страницы физической памяти, и в объёме целых страниц физической памяти;

Для распределения памяти под буфера DMA предоставляются несколько альтернативных групп API, их реализации полностью архитектурно зависимы, но вызовы создают уровень абстракций:

1. Coherent DMA mapping:

	void *dma_alloc_coherent( struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag ); 
	void dma_free_coherent( struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle ); 

- здесь не требуется распределять предварительно буфер DMA, этот способ применяется для устойчивых распределений многократно (повторно) используемых буферов.

2. Streaming DMA mapping:

	dma_addr_t dma_map_single( struct device *dev, void *ptr, size_t size,
	                           enum dma_data_direction direction ); 
	void dma_unmap_single( struct device *dev, dma_addr_t dma_handle, size_t size,
	                       enum dma_data_direction direction ); 

- где direction это направление передачи данных: PCI_DMA_TODEVICE, PCI_DMA_FROMDEVICE, PCI_DMA_BIDIRECTIONAL, PCI_DMA_NONE ; этот способ применяется для выделения под однократные операции.

3. DMA pool:

	#include <linux/dmapool.h>
	struct dma_pool *dma_pool_create( const char *name, struct device *dev,
	                 size_t size, size_t align, size_t allocation );
	void dma_pool_destroy( struct dma_pool *pool ); 
	void *dma_pool_alloc( struct dma_pool *pool, gfp_t mem_flags, dma_addr_t *handle );
	void dma_pool_free( struct dma_pool *pool, void *vaddr, dma_addr_t handle ); 

- часто необходимо частое выделение малых областей для DMA обмена, dma_alloc_coherent() допускает минимальное выделение в одну физическую страницу; в этом случае оптимальным становится dma_pool().

4. Старый (перешедший из ядра 2.4) API, PCI-специфический интерфейс — два (две пары вызовов) метода, аналогичных, соответственно п.1 и п.2:

	void *pci_alloc_consistent( struct device *dev, size_t size, dma_addr_t *dma_handle ); 
	void pci_free_consistent( struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle ); 
	dma_addr_t pci_map_single( struct device *dev, void *ptr, size_t size, int direction ); 
	void pci_unmap_single( struct device *dev, dma_addr_t dma_handle, size_t size, int direction ); 

Примеры использования API однотипны и достаточно громоздки. Некоторые примеры использования показаны в архиве dma.tgz, результаты выполнения показаны там же в файле dma.hist.


Предыдущий раздел: Оглавление Следующий раздел:
Отображение памяти   Устройства USB