LINUX.ORG.RU

Данные из DMA


0

0

Запустил DMA PCI-устройства. Принимаю данные в адресное пространство ядра (в драйвер). DMA буфер получаю так:
dma_buff = __get_dma_pages(GFP_KERNEL, PAGE_ORDER);
dma_buff_phys = __pa(dma_buff);
Контроллеру скармливаю dma_buff_phys, а данные потом читаю из dma_buff. Все нормально. Но хотелось бы, чтоб по DMA данные писались прямо в пространство пользовательского процесса. Как это можно сделать??? Вот родилась идея: в пользовательском процессе выделить буфер, его адрес (виртуальный) в драйвер, там получить физический и его в контроллер. Вот только вопрос как из виртуального физ. получить (вернее логический, физ. то я из логического получу __pa()) и со страницами, чтоб все нормально было???


Ответ на: комментарий от blackice

Но ведь io_remap_page_range проходит для регистров PCI, т.е. я ими из пользовательского процесса могу управлять, вот и для памяти может есть способ ?

senjy
() автор топика
Ответ на: комментарий от senjy

Может mmap() из user space? А в драйвере что-то типа
addr = virt_to_phys(mybuf->data);
remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, size, vma->vm_page_prot)

sts
()
Ответ на: комментарий от idle

> 1) Нужно выделить память для DMA в режиме Bus mastering на PCI шине. >Сейчас память выделяется в ядре. Процесс через ioctl сообщает модулю >ядра о желании получить данные по DMA. Модуль выделяет блоки памяти и >передает адреса этих блоков назад процессу. После чего процесс делает >для этих блоков mmap(). После запускает DMA и считывает данные >напрямую из этих блоков.

Вот это то, что мне нужно. Но у меня не работает.
1. Выделя в ядре буфер:
dma_buff = __get_dma_pages(GFP_KERNEL, PAGE_ORDER);
dma_buff_phys = __pa(dma_buff);
2. Делаю метод mmap(), в котором передаю dma_buff_phys процессу через
physical = dma_buff_phys + off;
io_remap_page_range(vma, vma->vm_start, physical, vsize, vma->vm_page_prot);
И нифига - в модуле данные получаю, в процессе нет... Что делаю не так???

Вот не совсем понял:
>Модуль выделяет блоки памяти и передает адреса этих блоков назад >процессу. После чего процесс делает для этих блоков mmap().
Т.е. он передает через ioctl адрес буфера в процесс (какой адрес физ. или лог. ???), а там .... не понимаю. Поясните пожалуйста этот фрагмент может мне этого не хватает...

senjy
() автор топика
Ответ на: комментарий от senjy

> Что делаю не так???

вы все делаете не так.

> dma_buff = __get_dma_pages(GFP_KERNEL, PAGE_ORDER);

забудьте. 

> io_remap_page_range(vma, vma->vm_start, physical, vsize, vma->vm_page_prot)

не будет работать без PageReserved

idle ★★★★★
()
Ответ на: комментарий от idle

Блин... Сейчас использую pci_map_single ... Фигня какая-то, вообще приема нет. Можно указать последовательность действий: Какую ф-цию использовать для выделения памяти, какой адрес контроллеру передать, какой адрес в процесс...

senjy
() автор топика
Ответ на: комментарий от senjy

Для выделения памяти использовать pci_alloc_consistent() (если 2.4)dma_aloc_consistent() (если 2.6). Они вернут пару адресов - Физический и виртуальный адрес. После выделения буфера в ядре нужно еще установить PageReserved для выделенного блока памяти. Потом физический адрес передать в процесс пользователя и там сделать mmap() указав в offset этот физический адрес. На этом форуме уже это обсуждалось. Поищите.

karak
()
Ответ на: комментарий от karak

Вот куски кода, модуля ядра. У меня рботало. 

int dev_mmap(struct file *file, struct vm_area_struct *vma )
{
        int x = 0;

        unsigned long offset = VMA_OFFSET(vma);

        vma->vm_flags |= VM_RESERVED;

        x = remap_page_range(vma, vma->vm_start, offset,
                              vma->vm_end-vma->vm_start,
                              vma->vm_page_prot);

        if( x < 0) {
          printk("<0>remap_page_range error.\n" );
      return -EAGAIN;
        }

        return 0;
}

//выделение блоков памяти
for( ii=0; ii<(int)blkNum; ii++ )
	{		
		m_arrBlock[ii].sysAdr = pci_alloc_consistent(                                                                        m_pci, 
							  		m_blkSize, 
							  		&m_arrBlock[ii].phyAdr );
		if( m_arrBlock[ii].sysAdr == NULL ) 
		{
			printk("<0>  Alloc() failed to allocate block=%d\n", ii );
			return -ENOMEM;
		}
		
		//try to lock all physical pages in the current block
		lock_pages( m_arrBlock[ii].sysAdr, m_blkSize );		
	}

//установка SetPageReseved
int lock_pages( void *va, u32 size )
{
	int j=0; 
	
	struct page *start_page_addr = virt_to_page( va );
		
	spin_lock(&init_mm.page_table_lock);
	for (j=0; j < (size >> PAGE_CACHE_SHIFT); j++) {
		SetPageReserved((start_page_addr+j));
		//printk("<0> start_page_addr[%i] = 0x%X\n", j, (int)                      (start_page_addr+j) );
	}
	spin_unlock(&init_mm.page_table_lock);	
	//set lock status;
		
	return 0;	
} 

//вызов mmap из программы пользователя
pDev->pDevMemoryVA =  (U32*)mmap(NULL,
					 (size_t)size,
					 PROT_READ | PROT_WRITE,
					 MAP_SHARED,
					 fd,
					 (off_t)phys_addr );


Очистку ресурсов приводит не буду... (Заодно узнаю что не так (Спасибо))

karak
()
Ответ на: комментарий от karak

Спасибо за подробный ответ.
В пространстве ядра все нормально, прием идет, данные вижу.
А вот в пользовательской проге данные получить не могу.
Делаю так:

dma_addr = pci_alloc_consistent(dev, Size, ph_addr);
start_page_addr = virt_to_page( dma_addr );
SetPageReserved(start_page_addr);
Передаю ph_addr проге.

В проге вызываю mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, ph_addr);

В методе mmap в модуле делаю:
off = vma->vm_pgoff << PAGE_SHIFT;
physical = ph_addr + off;
vsize = vma->vm_end - vma->vm_start;
psize = simple_region_size - off;

io_remap_page_range(vma, vma->vm_start, physical, vsize, vma->vm_page_prot);

В юзер проге данных не вижу - одни нули!!! Подскажите.......


senjy
() автор топика
Ответ на: комментарий от senjy

> dma_addr = pci_alloc_consistent(dev, Size, ph_addr);

ph_addr - это адрес? из фрагмента не понятно.

Вот макрос который я не привел в предыдущем посте:
#define VMA_OFFSET(vma)  ((vma)->vm_pgoff << PAGE_SHIFT)

SetPageReserved нужно сделать для всего блока Size, при условии, что он больше чем страница.

Должно работать... %-)

karak
()
Ответ на: комментарий от karak

Если данные не видно в юзер-спэйсе, то не работает mmap. Надо разобраться с ним. 

> SetPageReserved нужно сделать для всего блока Size, а он может быть больше чем страница.

в vma надо установить флаг:

vma->vm_flags |= VM_RESERVED;

karak
()
Ответ на: комментарий от senjy

проверяете что возвращает mmap() в user space?
может ещё добавить msync()?

sts
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.