前言
一个基础的petalinux工程,在配置工程的时候即可在图形界面进行kernel配置,然后编译出内核镜像,并且要在编译完整个工程后才有内核源码,且目录部分与传统Soc的SDK kernel部分不同。在驱动开发中,如果想要单独写驱动并在Xilinx平台上运行,该怎样操作呢?
一、字符设备基础
字符设备:是指只能按byte进行读写操作的设备,以字节为单位进行数据传输,不能随机读取设备中的某一数据、读取数据要按照先后数据。字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台和LED等。
一般每个字符设备或者块设备都会在/dev目录(可以是任意目录,这样是为了统一)下对应一个设备文件。linux用户层程序通过设备文件来使用驱动程序操作字符设备或块设备。
二、开发环境
petalinux版本:2022.2
kernel版本:5.15
开发板:ZCU106
三、编写驱动
1.内核目录
编译内核模块,最关键的是找到内核源码路径和选择好编译器,以上述版本为例,编译完petalinux工程后,生成的内核源码路径如下:
/home/lzw/petalinux/project/zcu106_demo/build/tmp/work/xilinx_zcu106-xilinx-linux/linux-xlnx/5.15.36+gitAUTOINC+19984dd147-r0/linux-xilinx_zcu106-standard-build
2.编写驱动
重要的结构体:Linux下一切皆是“文件”,struct file_operations中的成员函数会在用户层进行open(),read(),write(),close()等系统调用时最终被内核驱动调用。就像应用层写数据直接用系统调用的接口write(),不用关心对应驱动部分的write()是怎么实现的。下面列出一些常用的成员:
struct file_operations {
struct module *owner; //THIS_MODULE
loff_t (*llseek) (struct file *, loff_t, int); //修改读写位置
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); //读取文件
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); //写入文件
unsigned int (*poll) (struct file *, struct poll_table_struct *); //轮询,查询是否可以非阻塞读写
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); //32位ioctl
long (*compat_ioctl) (struct file *, unsigned int, unsigned long); //64位ioctl
int (*mmap) (struct file *, struct vm_area_struct *); //内存映射,帧缓冲用的多
int (*open) (struct inode *, struct file *); //打开设备
int (*release) (struct inode *, struct file *); //释放设备,对应close()
int (*fsync) (struct file *, loff_t, loff_t, int datasync); //刷新数据
int (*fasync) (int, struct file *, int); //异步刷新数据
...
};
搭建字符设备驱动框架,常用的几个接口如下:
static int __init test_init(void)