Удалось отмапить буфер ядра в user space, но данные в буфере юзера циклически повторяются каждые 4096 байт.
В ядре маплю как-то так:
Код
static u64* dma_buf_virt_core_addr_ptr;
static dma_addr_t dma_bus_addr_for_fpga;
#ifndef VM_RESERVED
#define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
#endif
struct mmap_info
{
uint64_t* data;
int reference;
};
void mmap_open(struct vm_area_struct* vma)
{
struct mmap_info* info = (struct mmap_info*)vma->vm_private_data;
info->reference++;
}
void mmap_close(struct vm_area_struct* vma)
{
struct mmap_info* info = (struct mmap_info*)vma->vm_private_data;
info->reference--;
}
static int mmap_fault(struct vm_area_struct* vma, struct vm_fault* vmf)
{
// если в user space был отмаплен буфер с помощью функции mmap, в которую передали размер буфера 65536,
// то здесь (vma->vm_end - vma->vm_start) как раз будет равно 65536,
// то есть делаю вывод, что vma соотносится с созданным буфером в user space.
struct page* page;
struct mmap_info* info;
info = (struct mmap_info*)vma->vm_private_data;
if (!info->data)
{
printk( KERN_INFO "[ag_pcie_driver]:[mmap_fault]: no data");
return 0;
}
//get the page
page = virt_to_page(info->data);
// increment the reference count of this page
get_page(page);
vmf->page = page;
return 0;
}
struct vm_operations_struct mmap_vm_ops =
{
.open = mmap_open,
.close = mmap_close,
.fault = mmap_fault,
};
int mmapfop_mmap(struct file* filp, struct vm_area_struct* vma)
{
vma->vm_ops = &mmap_vm_ops;
vma->vm_flags |= VM_RESERVED;
vma->vm_private_data = filp->private_data;
mmap_open(vma);
return 0;
}
int mmapfop_close(struct inode* inode, struct file* filp)
{
filp->private_data = NULL;
return 0;
}
int mmapfop_open(struct inode* inode, struct file* filp)
{
struct mmap_info* info = kmalloc(sizeof(struct mmap_info),GFP_KERNEL);
info->data = dma_buf_virt_core_addr_ptr;
filp->private_data = info;
return 0;
}
static const struct file_operations mmap_fops =
{
.owner = THIS_MODULE,
.open = mmapfop_open,
.release = mmapfop_close,
.mmap = mmapfop_mmap
};
static struct miscdevice ag_user_struct_miscdevice3 =
{
MISC_DYNAMIC_MINOR,
"ag_pcie_driver3",
&mmap_fops
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int ag_pciedr_probe( struct pci_dev *dev, const struct pci_device_id *id )
{
....
// выделяю буфер в пространстве ядра
dma_buf_virt_core_addr_ptr = pci_alloc_consistent( dev, 4194304, &dma_bus_addr_for_fpga);
// передаю в ПЛИС шинный адрес
iowrite32( (u32)((dma_bus_addr_for_fpga)) , 0x10); //low
iowrite32( (u32)((dma_bus_addr_for_fpga>>32)&0xffffffff), 0x14); //high
....
}
........
В user space открываю устройство и маплю буфер:
Код
ssize_t g_file_fid3 = open ("/dev/ag_pcie_driver3", O_RDWR);
char* g_ptr_mmap3 = (char*)mmap( NULL, 65536, PROT_READ | PROT_WRITE, MAP_SHARED, g_file_fid3, 0);
uint32_t* ptr = (uint32_t*)g_ptr_mmap3; // для удобства
Теперь засылаю данные из ПЛИС на комп.
Засылаю, например, 100 слов по 32 бит, начиная с 0 адреса.
В ядре вывожу на печать содержимое ядерного буфера dma_buf_virt_core_addr_ptr.
В нем все нормально - первые 100 слов из ПЛИС, дальше все нули.
Печатаю в user space буфер ptr с 0 по 4096 слова.
Вижу первые 100 слов из ПЛИС, дальше нули до 1024 слова.
С 1024 слова опять 100 слов из ПЛИС, дальше нули до 2048 слова.
И т.д.
Как будто первая страница в 4096 Байт (а это 1024 слова по 32 бит) циклически выделяется и повторяется для всего моего буфера g_ptr_mmap3.
Само отображение ядерного буфера в пользовательский буфер происходит в методе mmap_fault.
Если я обращаюсь к g_ptr_mmap3, то я попадаю в mmap_fault.
Мой ядерный адрес лежит в info->data.
Преобразование адреса в номер страницы происходит в строке
page = virt_to_page(info->data);
И получается, что я всегда нахожу первую страницу для моего ядерного адреса.
Как я думаю, нужно как то добавить нужное смещение в эту строчку и все заработает.
Даже нашел, что в переменной vmf->pgoff хранится номер страницы пользовательского буфера, к которой я и обращаюсь.
Но правильно реализовать мозгов не хватает...
Пробовал просто добавить смещение в строчку
page = virt_to_page(info->data+4096);
Но данные в пользовательском буфере все равно не корректны, к тому же это иногда приводит зависанию компа).
Подскажите, как правильно отмапить нужную страницу, осталось то вроде чуть чуть.