Перейти к содержанию
    

Народ, подскажите, пожалуйста, как правильно использовать 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.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

Изменено пользователем sasamy

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Разобрался.

Надо было физический адрес не из виртуального через 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;

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Изменено пользователем sasamy

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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 тоже 🙂

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Как корректно маппить регистры 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 😞

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

IMG_20230503_165701.thumb.jpg.f116dc868b3a939950f136e5800228f7.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

В функции:

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:

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Изменено пользователем sasamy

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Изменено пользователем sasamy

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...