一、Linux驱动概述

驱动本质

Linux 驱动运行在内核空间,作为硬件、操作系统、上层应用的通信桥梁;应用通过 open/read/write 库函数发起系统调用,陷入内核后由驱动操作硬件,区分用户空间、内核空间。

image

驱动分类

  • 字符设备:按字节串行访问(LED、按键等,本教程核心)。
  • 块设备:块为单位随机读写(硬盘、SD 卡)。
  • 网络设备:专用数据包接口,不使用标准文件读写函数。

二、驱动开发(LED实操案例)

2.1 字符设备驱动开发

整体开发步骤
查看硬件手册 / 原理图 → 修改设备树 → 编写驱动框架代码 → 编译驱动(Petalinux工程内置 / 独立 Makefile) → 编写上层测试程序 → 开发板运行调试。

核心知识点

  • file_operations 结构体
    image

  • 设备号管理

每个设备文件都有主次设备号major,主设备号是唯一的,每个主设备下有次设备号minor,次设备号在这个主设备下也是唯一的。在Linux系统下输入cat /proc/devices命令可以查看已被注册的主设备号。

示例:

int major = 200;
int minor = 0;
dev_t devid = MKDEV(major, minor);   //通过主次设备号获取设备号
/* 向内核注册设备号 */
register_chrdev_region(devid, 1, “xxx-dev”);
dev_t  devid;    //设备号
/* 申请设备号 */
alloc_chrdev_region(&devid, 0, 1, "xxx-dev"); 
  • 字符设备注册流程

① 使用cdev结构体来定义一个字符设备,它定义在include/linux/cdev.h中

struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
} __randomize_layout;

② cdev结构体变量定义后需要用cdev_init()函数初始化,函数原型为

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

③ 初始化字符结构体变量后,便可以用这个变量向Linux系统注册字符设备。注册函数 cdev_add原型为:

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

④ 字符设备的注销函数是 cdev_del 函数,原型为:

void cdev_del(struct cdev *p)
  • 驱动模块出入口
module_init(xxx_init);    //注册模块加载函数
module_exit(xxx_exit);    //注册模块卸载函数
  • 设备节点自动创建

类的创建和删除:
创建设备前需要先创建类,设备是在类下面创建的。类的结构体struct class结构体定义在include/linux/device/class.h中。
使用函数class_create()来创建类,原型为:

struct class *  class_create(const char *name);

使用函数class_destroy()来删除类,原型为:

void class_destroy(const struct class *cls)

设备节点的创建和删除:
创建类后,使用device_create()函数在类下面创建设备,原型为:

device_create(const struct class *cls, struct device *parent, dev_t devt,
      void *drvdata, const char *fmt, ...);

使用函数device_destroy()来删除设备节点,原型为:

void device_destroy(const struct class *cls, dev_t devt);

2.2 platform驱动框架

驱动分离思想
Platform 虚拟总线实现硬件资源(SOC 底层)与业务设备(LED / 按键)分离。

  • SOC 厂商统一实现底层 GPIO 硬件驱动
  • 外设厂商仅编写设备业务逻辑,通过统一接口调用底层资源
  • 更换硬件平台仅替换底层 SOC 驱动,上层设备代码无需大幅修改

platform三大核心组件

  1. platform_bus 虚拟总线
    内核内置 platform_bus_type,提供 match 匹配逻辑,自动绑定同名字的设备与驱动。

  2. platform_driver 驱动
    核心成员 probe(设备匹配成功执行初始化)、remove(卸载释放资源),通过 platform_driver_register / unregister 完成注册注销。

  3. platform_device 设备
    描述硬件资源(寄存器地址、引脚),支持手动注册结构体或设备树替代;驱动通过 platform_get_resource() 读取硬件资源。


三、UIO驱动概述

UIO 核心思想是轻量内核驱动,业务逻辑放在用户态,适合 FPGA 自定义 IP、工业 IO 卡这类只需要访问硬件内存、处理中断的设备;
内核只做基础资源管理,降低内核模块 bug 风险。内核暴露 /dev/uioX 字符设备、/sys/class/uio/uioX sysfs 节点给应用程序。


四、UIO 核心框架 uio_core

核心数据结构uio_info

struct uio_info {
struct uio_device		*uio_dev;
const char					*name;
const char					*version;
struct 		uio_mem		mem[MAX_UIO_MAPS];
struct 		uio_port		port[MAX_UIO_PORT_REGIONS];
long		irq;
unsigned long 	irq_flags;
void		*priv;
irqreturn_t (*handler)(int irq, struct uio_info *dev_info);
int (*mmap)(struct uio_info *info, struct vm_area_struct *vma);
int (*open)(struct uio_info *info, struct inode *inode);
int (*release)(struct uio_info *info, struct inode *inode);
int (*irqcontrol)(struct uio_info *info, s32 irq_on);
};

核心数据结构uio_device

struct uio_device {
struct module           *owner;
struct devicedev;     
int                     minor;     
atomic_t                event;     
struct fasync_struct    *async_queue;  
wait_queue_head_t       wait;      
struct uio_info         *info;     
struct mutexinfo_lock;     
struct kobject          *map_dir;  
struct kobject          *portio_dir;  
};

讲解 UIO 设备注册 API __uio_register_device
内核实现 uio 文件操作集合(open/read/mmap/poll 等);
中断分为顶半部处理回调、底半部线程,中断事件通知用户空间。


五、通用 UIO 设备驱动 uio_pdrv_genirq

内核自带的 platform 通用 UIO 驱动,无需自定义编写内核模块,通过设备树或 platform_data 配置即可使用。
实现了中断 handler、中断控制回调;支持设备树 OF 匹配,通过模块参数 of_id 匹配设备。


六、FPGA 自定义 IP 的 UIO 实操

以 PWM 自定义 IP 为例,给出寄存器地址表;
讲解完整使用流程:

  1. 开启内核 UIO 配置
  2. 设置 of_id 参数
  3. 修改设备树设置 compatible = "generic‑uio"
  4. 编写用户态测试程序
  5. 板卡运行测试,在用户空间操作 FPGA 寄存器、捕获中断

💡 完整视频讲解可前往 ALINX 视频号进行直播回看

更多细节欢迎关注我们黑金云课堂全年免费直播课
黑金云课堂八月直播日历 我们将在每周二、三、四,同步推进 Verilog开发、Vitis开发、Linux开发三大系列,带你从零开始,稳扎稳打掌握 FPGA 开发全流程!

系列 内容定位
Verilog开发 硬件描述语言基础、逻辑设计、仿真调试
Vitis开发 Zynq软硬件协同、外设驱动、网络协议栈
Linux开发 嵌入式Linux系统移植、驱动编写、应用开发
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐