Jump to content
    

Народ, подскажите, пожалуйста, как правильно использовать dma_mmap_coherent на ARM (RK3588).

Пытаюсь маппить из kernel в userspace следующим образом:

static int dma_dev_remap_mmap(struct file *filp, struct vm_area_struct *vma)
{
	dma_dev_dev_t* dma_dev_dev_ptr = 0;
	dma_dev_dev_ptr = (dma_dev_dev_t*)filp->private_data;

#ifdef VM_RESERVED
	vma->vm_flags |= (VM_PFNMAP | VM_SHARED | VM_RESERVED);
#else
	vma->vm_flags |= (VM_PFNMAP | VM_SHARED | (VM_DONTEXPAND | VM_DONTDUMP) );
#endif

	..........

	vma->vm_pgoff = 0;
	if(dma_mmap_coherent(&(dma_dev_dev_ptr->pci_dev->dev),
		               vma,
		               (void*)(vma->vm_start),
		               virt_to_phys(dma_dev_dev_ptr->tx_dma_buffers[index_tx_buffer].base_va),
		               (size_t)(vma->vm_end - vma->vm_start)))
	{
		printk(KERN_ALERT "dma_dev: tx_dma_buffers map error!");
		return -EAGAIN;
	}

	vma->vm_ops = &dma_dev_remap_vm_ops;
	
	..........
}

Но в userspace получаю какой-то левый указатель.

В драйвере при создании буфера я забиваю его магическими числами 0xEB, а из userspace читается 0x00.

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

Share this post


Link to post
Share on other sites

Помоему адрес неправильно указываете - надо виртуальный адрес буфера в ядре а не юзерспейс

Quote
	if(dma_mmap_coherent(&(dma_dev_dev_ptr->pci_dev->dev),
		               vma,
	>>>>>	               dma_dev_dev_ptr->tx_dma_buffers[index_tx_buffer].base_va, // (void*)(vma->vm_start),
		               virt_to_phys(dma_dev_dev_ptr->tx_dma_buffers[index_tx_buffer].base_va),
		               (size_t)(vma->vm_end - vma->vm_start)))
	

 

 

https://elixir.bootlin.com/linux/latest/source/sound/core/memalloc.c#L480

Edited by sasamy

Share this post


Link to post
Share on other sites

Разобрался.

Надо было физический адрес не из виртуального через virt_to_phys получать, а брать тот что возвращает dma_alloc_coherent.

Ну и ещё момент. Если делать универсально ARM/x86, для для x86 ветки нужно использовать remap_pfn_range.

		vm_pgoff_old = vma->vm_pgoff;
#if defined(__aarch64__) || defined(__arm__)
		vma->vm_pgoff = 0;
		if(dma_mmap_coherent(&(dma_dev_dev_ptr->pci_dev->dev),
		                   vma,
		                   (void*)(vma->vm_start),
		                   dma_dev_dev_ptr->tx_dma_buffers[index_tx_buffer].base_ba,
		                   (size_t)(vma->vm_end - vma->vm_start)))
#else
		if(remap_pfn_range(vma, vma->vm_start,
		                   page_to_pfn(virt_to_page(dma_dev_dev_ptr->tx_dma_buffers[index_tx_buffer].base_va)),
		                   vma->vm_end - vma->vm_start,
		                   vma->vm_page_prot))
#endif
		{
			printk(KERN_ALERT "dma_dev: tx_dma_buffers map error!");
			return -EAGAIN;
		}
		vma->vm_pgoff = vm_pgoff_old;

 

Share this post


Link to post
Share on other sites

On 4/28/2023 at 12:05 PM, BSACPLD said:

Разобрался.

не совсем

On 4/28/2023 at 12:05 PM, BSACPLD said:

Надо было физический адрес не из виртуального через virt_to_phys получать, а брать тот что возвращает dma_alloc_coherent.

dma_alloc_coherent возвращает виртуальный адрес для пространства ядра - его и надо вместо

(void*)(vma->vm_start),

соответственно и физический адрес надо брать тот который она инициализирует через указатель dma_addr_t *dma_handle в параметрах вызова

Quote

static inline void *dma_alloc_coherent(struct device *dev, size_t size,
        dma_addr_t *dma_handle, gfp_t gfp)

https://elixir.bootlin.com/linux/latest/source/include/linux/dma-mapping.h#L420

On 4/28/2023 at 12:05 PM, BSACPLD said:

Ну и ещё момент. Если делать универсально ARM/x86, для для x86 ветки нужно использовать remap_pfn_range.

если ядро старше 3.6, а на сегодня это практически всегда - не надо

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=64ccc9c033c6089b2d426dad3c56477ab066c999

Edited by sasamy

Share this post


Link to post
Share on other sites

1 hour ago, sasamy said:

если ядро старше 3.6, а на сегодня это практически всегда - не надо

На практике у меня получилось обратное.

На x86 dma_mmap_coherent некорректно отрабатывает.

Приходится использовать remap_pfn_range для x86 ветки.

UPD.

Вы были правы.

Если вместо (void*)(vma->vm_start) передавать виртуальный адрес от dma_alloc_coherent, то dma_mmap_coherent работает и на x86 тоже 🙂

Share this post


Link to post
Share on other sites

Ещё один вопрос возник.

Как корректно маппить регистры BAR PCIe устройства чтобы это работало и на x86 и на ARM?

Вот такой код:

		if(remap_pfn_range(vma, vma->vm_start,
		                   PHYS_PFN(dma_dev_dev_ptr->regs_pa[0]),
		                   vma->vm_end - vma->vm_start,
		                   vma->vm_page_prot))
		{
			printk(KERN_ALERT "dma_dev: reg map BAR0_REGS_BASE_ADDR error!");
			return -EAGAIN;
		}

работает только на x86 😞

Share this post


Link to post
Share on other sites

On 4/29/2023 at 12:27 PM, BSACPLD said:

Как корректно маппить регистры BAR PCIe устройства чтобы это работало и на x86 и на ARM?

в ядре начиная с 4.12 есть кроссплатформенная pci_mmap_resource_range

Quote

https://elixir.bootlin.com/linux/v6.3/source/include/linux/pci.h#L1977

 

 

Share this post


Link to post
Share on other sites

Странная проблема...

На RK3588 на компьютере от Radxa работает без проблем, а на том же RK3588 на компьютере от Firefly зависает при вызове mmap.

При этом dmesg выдает следующую информацию об ошибке:

IMG_20230503_165701.thumb.jpg.f116dc868b3a939950f136e5800228f7.jpg

Share this post


Link to post
Share on other sites

Нашел причину.

В функции:

static int dma_dev_remap_mmap(struct file *filp, struct vm_area_struct *vma)
{
	unsigned long vm_pgoff_old;
	
	dma_dev_dev_t* dma_dev_dev_ptr = 0;
	dma_dev_dev_ptr = (dma_dev_dev_t*)filp->private_data;

#ifdef VM_RESERVED
	vma->vm_flags |= (VM_PFNMAP | VM_SHARED | VM_RESERVED);
#else
	vma->vm_flags |= (VM_PFNMAP | VM_SHARED | (VM_DONTEXPAND | VM_DONTDUMP) );
#endif


	...


	if(direction == PC_2_DEV)
	{
		...
	} 

	if(direction == DEV_2_PC)
	{
		printk(KERN_NOTICE "dma_dev: tx_dma_buffers map %lx\n", vma->vm_pgoff);

		if(dma_dev_dev_ptr->tx_dma_buffers[index_tx_buffer].base_va == NULL)
		{
			printk(KERN_ALERT "dma_dev: tx_dma_buffers[%d].base_va == NULL\n", index_tx_buffer);
			direction = NO_TRANSFER;
			return -EAGAIN;
		}

		vm_pgoff_old = vma->vm_pgoff;

		vma->vm_pgoff = 0;
		if(dma_mmap_coherent(&(dma_dev_dev_ptr->pci_dev->dev),
		                   vma,
		                   dma_dev_dev_ptr->tx_dma_buffers[index_tx_buffer].base_va,
		                   dma_dev_dev_ptr->tx_dma_buffers[index_tx_buffer].base_ba,
		                   (size_t)(vma->vm_end - vma->vm_start)))
		{
			printk(KERN_ALERT "dma_dev: tx_dma_buffers map error!");
			return -EAGAIN;
		}

		vma->vm_pgoff = vm_pgoff_old;

		direction = NO_TRANSFER;

		vma->vm_ops = &dma_dev_remap_vm_ops;
		dma_dev_vma_open(vma);
		return 0;
	}


	return -EAGAIN;
}

через указатель некорректно передается структура с дескриптором устройства:

dma_dev_dev_ptr = (dma_dev_dev_t*)filp->private_data;

На x86, Radxa Rockchip работает, а на Firefly Rockchip - нет.

Но если передать структуру с дескриптором устройства просто через глобальную переменную:

dma_dev_dev_ptr = &(dma_dev_dev[0]);

то корректно работает на всех выше перечисленных платформах.

Может быть я как-то некорректно работаю с данным указателем или неправильно сохраняю указатель на структуру?

// Открытие файла устройства
int dma_dev_open(struct inode *inode, struct file *filp)
{
	dma_dev_dev_t* dev; /* device information */

	printk(KERN_NOTICE "dma_dev_open.\n");
	// Сохраняем указатель на структуру
	dev = container_of(inode->i_cdev, dma_dev_dev_t, cdev);
	filp->private_data = dev; /* for other methods */

	return 0;          /* success */
}
// Инициализация для устройства dma_dev_dev_t структуры cdev и ее регистрация в ОС
static void dma_dev_setup_cdev(dma_dev_dev_t *dev, int index)
{
	int err, devno = MKDEV(dma_dev_major, dma_dev_minor + index);
	//printk(KERN_NOTICE "dma_dev_setup_cdev");   
	cdev_init(&dev->cdev, &dma_dev_fops);
	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops = &dma_dev_fops;
	err = cdev_add (&dev->cdev, devno, 1);

	if(err)
	{
		printk(KERN_ALERT "dma_dev: error %d at adding dma_dev%d", err, index);
	}
}

Коллеги, нужна ваша помощь :help:

Я в упор не вижу в чем проблема :dash2:

 

Share this post


Link to post
Share on other sites

On 5/5/2023 at 11:02 PM, BSACPLD said:
MKDEV(dma_dev_major, dma_dev_minor + index);

откуда берёте dma_dev_major, dma_dev_minor ? устройство похоже нестандартное.

Share this post


Link to post
Share on other sites

21 minutes ago, sasamy said:

откуда берёте dma_dev_major, dma_dev_minor ? устройство похоже нестандартное.

Из define присваиваются константы.

static int dma_dev_major = DEV_MAJOR;					// < Major ID для символьного устройства
static int dma_dev_minor = DEV_MINOR;					// < Minor ID для символьного устройства
#define DEV_MAJOR                           (83)
#define DEV_MINOR                           (12)

Устройство действительно нестандартное - специализированный сопроцессор.

Поэтому и свой драйвер.

DMA там тоже свой. Из готовых IP только AXI Memory Mapped To PCI Express.

И если это важно, устройство в системе только одно.

Share this post


Link to post
Share on other sites

On 5/6/2023 at 2:53 PM, BSACPLD said:

Устройство действительно нестандартное - специализированный сопроцессор.

ошибок я не вижу, но если бы писал сам то взял готовый класс устройств misc device - готовая обёртка для необычных устройств над голым char device

https://embetronicx.com/tutorials/linux/device-drivers/misc-device-driver/

там пример немного куцый, приватные данные автоматом цепляются - свою open не надо дописывать

https://docs.kernel.org/6.1/driver-api/misc_devices.html

Quote

The structure passed is linked into the kernel and may not be destroyed until it has been unregistered. By default, an open syscall to the device sets file->private_data to point to the structure. Drivers don't need open in fops for this.

чтобы потом получать указатель на свою структуру данных аналогично с cdev надо включить  в неё miscdev

https://elixir.bootlin.com/linux/v6.2.14/source/drivers/misc/pci_endpoint_test.c#L116

https://elixir.bootlin.com/linux/v6.2.14/source/drivers/misc/pci_endpoint_test.c#L86

https://elixir.bootlin.com/linux/v6.2.14/source/drivers/misc/pci_endpoint_test.c#L727

Edited by sasamy

Share this post


Link to post
Share on other sites

On 4/29/2023 at 1:46 PM, sasamy said:

в ядре начиная с 4.12 есть кроссплатформенная pci_mmap_resource_range

Можно пример использования?

Беглый поиск примеров в google ничего не дал 😞

Не пойму что передавать в mmap_state...

Share this post


Link to post
Share on other sites

On 5/12/2023 at 8:31 PM, BSACPLD said:

Беглый поиск примеров в google ничего не дал

неудивительно - это довольно специфичный случай отображать регистры устроства в юзерспейс - драйверы Linux в ядре работают

On 5/12/2023 at 8:31 PM, BSACPLD said:

Не пойму что передавать в mmap_state...

enum pci_mmap_state {
	pci_mmap_io,
	pci_mmap_mem
};

https://elixir.bootlin.com/linux/v6.2.15/source/include/linux/pci.h#L90

у вас похоже pci_mmap_mem но если не уверены - посмотрите BAR-ы своего устройства через lspci в юзерспейс, например для устройства с VID 1002

Quote

$ sudo lspci -nvvd 1002: | grep Region
    Region 0: Memory at fce0000000 (64-bit, prefetchable)
    Region 2: Memory at fcf0000000 (64-bit, prefetchable)
    Region 4: I/O ports at 1000
    Region 5: Memory at d0400000 (32-bit, non-prefetchable)

у вас там наверно от ксилинка VID 10ee

Quote

sudo lspci -nvvd 10ee: | grep Region

Quote

int pci_mmap_resource_range(struct pci_dev *dev, int bar,
                struct vm_area_struct *vma,
                enum pci_mmap_state mmap_state, int write_combine);

int bar - номер региона 0,1,2...

int write_combine - если нужна буферизованная запись передайте тут 1, но в общем случае для регистров тут надо 0

 

https://elixir.bootlin.com/linux/v6.2.15/source/drivers/pci/mmap.c#L22

Edited by sasamy

Share this post


Link to post
Share on other sites

Попробовал собрать с pci_mmap_resource_range:

ERROR: "pci_mmap_resource_range" [.../dma_dev.ko] undefined!

#include <linux/pci.h> включен, но pci_mmap_resource_range не видится при сборке.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...