通常这些非标准设备的驱动被实现为字符驱动。这些驱动使用了很多内核内部函数和宏。而这些内部函数和宏是变化的。这样驱动的编写者必须编写一个完全的内核驱动,而且一直维护这些代码。
而且这些驱动进不了主内核源码。于是就出现了用户空间I/O框架(Userspace I/O framework)。
UIO 怎样实现一个设备驱动的基本任务
一个设备驱动的主要任务有两个:
1. 存取设备的内存
2. 处理设备产生的中断
- 对于第一个任务,UIO 核心实现了mmap()可以处理物理内存(physical memory),逻辑内存(logical memory), 虚拟内存(virtual memory)。UIO驱动的编写是就不需要再考虑这些繁琐的细节。
- 第二个任务,对于设备中断的应答必须在内核空间进行。所以在内核空间有一小部分代码用来应答中断和禁止中断,但是其余的工作全部留给用户空间处理。
如果用户空间要等待一个设备中断,它只需要简单的阻塞在对 /dev/uioX的read()操作上。 当设备产生中断时,read()操作立即返回。UIO 也实现了poll()系统调用,你可以使用 select()来等待中断的发生。
select()有一个超时参数可以用来实现有限时间内等待中断。对设备的控制还可以通过/sys/class/uio下的各个文件的读写来完成。你注册的uio设备将会出现在该目录下。
假如你的uio设备是uio0那么映射的设备内存文件出现在 /sys/class/uio/uio0/maps/mapX,对该文件的读写就是 对设备内存的读写。
如下的图描述了uio驱动的内核部分,用户空间部分,和uio 框架以及内核内部函数的关系
/** * A structure describing the private information for a uio device. */ struct rte_uio_pci_dev { struct uio_info info;//uio 通用结构 struct pci_dev *pdev;//pci设备描述结构 enum rte_intr_mode mode;//中断模式 atomic_t refcnt; };
/**来自内核 uio_driver.h 文件 * struct uio_info - UIO device capabilities * @uio_dev: the UIO device this info belongs to * @name: device name * @version: device driver version * @mem: list of mappable memory regions, size==0 for end of list * @port: list of port regions, size==0 for end of list * @irq: interrupt number or UIO_IRQ_CUSTOM * @irq_flags: flags for request_irq() * @priv: optional private data * @handler: the device's irq handler * @mmap: mmap operation for this uio device * @open: open operation for this uio device * @release: release operation for this uio device * @irqcontrol: disable/enable irqs when 0/1 is written to /dev/uioX */ struct uio_info { struct uio_device *uio_dev;//uio设备 在 uio_register_device中初始化 const char *name;//名称 调用__uio_register_device之前必须初始化 const char *version;//版本号 //调用__uio_register_device之前必须初始化 struct uio_mem mem[MAX_UIO_MAPS];//可映射的内存区域列表,size == 0表示列表结束 struct uio_port port[MAX_UIO_PORT_REGIONS];//网口区域列表 long irq;//UIO_IRQ_CUSTOM 中断号 //分配给uio设备的中断号,调用__uio_register_device之前必须初始化 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);//中断控制操作 关闭/打开 当向/dev/uioX中写入值时 };
uio核心部分是一个名为"uio"的字符设备(下文称为“uio核心字符设备“)。用户驱动的内核部分使用uio_register_device向uio核心部分 注册uio设备。uio 核心的任务就是管理好这些注册的uio设备。这些uio设备使用的数据结构是 uio_device。而这些设备属性,比如name, open(), release()等操作都放在了uio_info结构中,用户使用 uio_register_device注册这些驱动之前 要设置好uio_info
igb_uio.ko初始化主要是做了两件事:
- 第一件事是配置中断模式;
- 第二种模式便是注册驱动
学习地址: Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂
更多DPDK相关学习资料有需要的可以自行报名学习,免费订阅,久学习,或点击这里加qun免费
领取,关注我持续更新哦! !
static struct pci_driver igbuio_pci_driver = { .name = "igb_uio", .id_table &