计划先整理下驱动的基础知识,再介绍下设备树相关dts文件的写法。
最简单的内核驱动代码示例
首先以最简单的内核驱动代码为例,介绍如何编写以及编译驱动代码。
内核驱动代码至少需要包含两个函数,init函数(insmod时被调用)、exit函数(rmmod时被调用)。
简单的内核驱动代码示例如下所示:
|
编译驱动需要相应的kernel headers,可以通过下面的指令安装系统的kernel header,kernel header的安装位置在/usr/src/linux。
sudo apt-get update |
下面是Makefile的示例,如下所示:
ifneq (${KERNELRELEASE},) |
与编译非内核驱动代码的Makefile对比,有两点让人疑惑的地方:
- KERNELRELEASE是什么?
- obj-m := module何时被执行?
其实在这里,make执行了两次,一次是执行当前目录下的Makefile,一次是在/usr/src/linux
下的Makefile。在当前目录下KERNELRELEASE
没有被定义,因此会执行else
下的语句,执行make不加参数的默认情况下会再执行default
中的内容,在default
这条语句中,通过make -C
进行目录跳转至内核源码目录读取那里的Makefile。M=${PWD}
表明返回当前目录从头重新开始读取当前目录的Makefile,编译内核源码文件生成对应的.o文件。从内核源码目录返回时, KERNELRELEASE
已被定义,make将读取else
之前的内容,联合中间文件生成.ko文件。编译完之后执行insmod module.ko
加载内核模块,通过rm module
删除模块,通过 dmesg
查看内核信息判断模块是否成功加载或删除。
在当前目录执行make
之后,生成一系列文件,device.ko device.mod.c device.mod.o device.o modules.order Module.symvers
,再执行sudo insmod device.ko
将模块加载到内核中,通过dmesg tail
显示最近的linux内核信息,如果输出Module start
说明模块成功加载。之后使用sudo rmmod device
删除该模块, 通过dmesg tail
显示最近的linux内核信息,如果输出Module end
说明模块成功删除。
稍微复杂的内核驱动代码示例
上面只是一个非常简单的示例,甚至没有将该驱动与设备进行绑定,下面给出一个完成的驱动代码示例。在编写驱动代码前,使用这条指令mknod -m 0666 /dev/mydev 240 0
创建mydev
设备,对应的major是240,minor是0。驱动代码如下所示:
|
Makefile与上文示例几乎一致,下面给出测试代码:
|
上面介绍了驱动代码的编写和编译,接下来介绍下xilinx的DMAENGINE和DMA的基础知识。
DMA基础知识
dma一般是内核空间下使用的驱动,用户空间比较复杂。
DMA有两种,Slave-DMA
、Async TX
:
Slave-DMA
:memory->device, device->memory, device->deviceslave的含义是指参与DMA传输的设备,对应的”master”是DMA controller本身
Async TX
:memory->memory
这里我们主要介绍slave DMA的使用方法,包括如下5个步骤,详细可以参考xilinx dma slave api:
申请一个DMA channel
DMA channel(结构:struct dam_chan),由DMA controller(provider)提供,被consumer(client,driver,slave driver)使用,client通过
struct dma_chan *dma_request_chan(struct device *dev, const char *name)
API申请channel,通过void dma_release_channel(struct dma_chan *chan)
API释放channle。配置DMA channel参数
driver申请到DMA channel后根据实际情况及DMA controller的能力配置channel,通过API
int dmaengine_slave_config(struct dma_chan *chan, struct dma_slave_config *config)
将struct dma_slave_config
告知DMA controller。
struct dma_slave_config {
enum dma_transfer_direction direction;
phys_addr_t src_addr;
phys_addr_t dst_addr;
enum dma_slave_buswidth src_addr_width;
enum dma_slave_buswidth dst_addr_width;
u32 src_maxburst;
u32 dst_maxburst;
bool device_fc;
unsigned int slave_id;
};代码中参数介绍:
direction:指明传输方向 memory->device, device->memory, device->device, memory->memory
src_addr:读取数据位置(src为mem不需要配置)
dst_addr:写入数据位置(dst为mem不需要配置)
src_addr_width、dst_addr_width:src/dst地址的宽度
src_maxburst、dst_maxburst:src/dst最大可传输的burst size(DMA控制的可缓存数据量大小)
device_fc:决定DMA传输是否结束的模块
slave_id:slave通过slave_id告诉dma controller自己的身份,一般情况下非必要,只要知道src、dst、len即可。
获取传输描述
通过API
dmaengine_prep_slave_single()
获得描述符dma_async_tx_descriptor
。struct dma_async_tx_descriptor {
dma_cookie_t cookie;
enum dma_ctrl_flags flags; /* not a 'long' to pack with cookie */
dma_addr_t phys;
struct dma_chan *chan;
dma_cookie_t (*tx_submit)(struct dma_async_tx_descriptor *tx);
int (*desc_free)(struct dma_async_tx_descriptor *tx);
dma_async_tx_callback callback;
void *callback_param;
struct dmaengine_unmap_data *unmap;
struct dma_async_tx_descriptor *next;
struct dma_async_tx_descriptor *parent;
spinlock_t lock;
};启动传输
获取传输描述符之后,通过
dmaengine_submit
API将描述符放到传输队列,返回唯一识别该标志符的cookie,再调用dma_async_issue_pending
API启动传输。等待传输结束
通过API
dma_async_is_tx_complete()
判断DMA事务是否完成。
基于Petalinux编译DMA驱动
请根据基于Xilinx zcu104版的petalinux移植linux(ffmpeg)这边文章认真配置环境
Pre-requisites
A zero-copy Linux driver and a userspace interface library for Xilinx’s AXI DMA and VDMA IP blocks. These serve as bridges for communication between the processing system and FPGA programmable logic fabric, through one of the DMA ports on the Zynq processing system.
git clone https://github.com/bperez77/xilinx_axidma.git
Steps
add hdf to ur petalinux project(这个hdf文件仅仅做了loopback,以下基于此loopback进行介绍)
petalinux-config --get-hw-description $(UR-HDF-PATH)
Image Packaging Configurations -> Root filesystem type -> SD card
取消勾选
DTG Settings->Kernel Bootargs->generate boot args automatically
,set bootargs toearlycon console=ttyPS0,115200 clk_ignore_unused root=/dev/mmcblk0p2 rw rootwait
petalinux-build
在编译过程中,如果卡在fsbl的编译,请执行以下两条指令
sudo apt install libgtk-3-dev
sudo apt install libgtk2.0-devcreate module
petalinux-create -t modules -n xilinx-axidma --enable
进入到git clone的文件夹路径,将所需文件拷贝到petalinux module的路径。这里涉及到三步,第一步拷贝文件,第二步删除不必要的文件,第三步是修改petalinux module文件夹的下的Makefile和.bb文件。
(1)将git clone下来的文件夹里的文件拷贝到petalinux module的路径
<path/to/PetaLinux/project>/project-spec/meta-user/recipes-modules/xilinx-axidma/files
cp -a driver/*.c driver/*.h include/axidma_ioctl.h <path/to/PetaLinux/project>/project-spec/meta-user/recipes-modules/xilinx-axidma/files
(2) 删除
xilinx-axidma.c
rm <path/to/PetaLinux/project>/project-spec/meta-user/recipes-modules/xilinx-axidma/files/xilinx-axidma.c
(3) 修改
<path/to/PetaLinux/project>/project-spec/meta-user/recipes-modules/xilinx-axidma/files/Makefile
删除Makefile第一行,取代为下面三行代码
DRIVER_NAME = xilinx-axidma
(DRIVER_NAME)-objs = axi_dma.o axidma_chrdev.o axidma_dma.o axidma_of.o
obj-m := $(DRIVER_NAME).o(4) 修改
<path/to/PetaLinux/project>/project-spec/meta-user/recipes-modules/xilinx-axidma/xilinx-axidma.bb
SRC_URI = "file://Makefile \
file://axi_dma.c \
file://axidma_chrdev.c \
file://axidma_dma.c \
file://axidma_of.c \
file://axidma.h \
file://axidma_ioctl.h \
file://COPYING \
"
create app
这里的app实现的功能是,将从千兆拿到的视频i帧转为yuv格式(基于ffmpeg)送到pl端,再将从pl端返回的结果送回内网的服务器。
change the system-user.dtsi according the pl.dtsi.
(1) 根据之前petalinux-build生成的
pl.dtsi
,将其中的内容全部复制到system-conf.dtsi
,这两个文件的地址均在<path/to/PetaLinux/project>/components/plnx_workspace/device-tree/device-tree
,按理说这部分复制进system-user.dtsi也可以,<path/to/PetaLinux/project>/components/plnx_workspace/device-tree/device-tree
这个目录下的文件一般是自动生成的。(2) 同时删除
system-top.dtsi
最后一行#include system-user.dtsi
(3) 修改system-user.dtsi(位置
/project-spec/meta-user/recipes-bsp/device-tree/files/”
),加入以下内容/ {
axidma_chrdev: axidma_chrdev@0 {
compatible = "xlnx,axidma-chrdev";
dmas = <&axi_dma_0 0 &axi_dma_0 1>;
dma-names = "tx_channel", "rx_channel";
};petalinux-build
petalinux-package --boot --fsbl ./images/linux/zynqmp_fsbl.elf --fpga ./images/linux/system.bit --pmufw ./images/linux/pmufw.elf --u-boot
,生成的文件BOOT.bin、image.ub
位于<path/to/PetaLinux/project>/images/linux/
下