微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > 嵌入式Linux设备驱动开发之:块设备驱动编程

嵌入式Linux设备驱动开发之:块设备驱动编程

时间:08-13 来源:3721RD 点击:

11.4 块设备驱动编程

块设备通常指一些需要以块(如512字节)的方式写入的设备,如IDE硬盘、SCSI硬盘、光驱等。它的驱动程序的编写过程与字符型设备驱动程序的编写有很大的区别。

块设备驱动编程接口相对复杂,不如字符设备明晰易用。块设备驱动程序对整个系统的性能影响较大,速度和效率是设计块设备驱动程要重点考虑的问题。系统中使用缓冲区与访问请求的优化管理(合并与重新排序)来提高系统性能。

1.编程流程说明

块设备驱动程序的编写流程同字符设备驱动程序的编写流程很类似,也包括了注册和使用两部分。但与字符驱动设备所不同的是,块设备驱动程序包括一个request请求队列。它是当内核安排一次数据传输时在列表中的一个请求队列,以最大化系统性能为原则进行排序。在后面的读写操作时会详细讲解这个函数,图11.5为块设备驱动程序的流程图,请读者注意与字符设备驱动程序的区别。

图11.5 块设备驱动程序流程图

2.重要数据结构

每个块设备物理实体由一个gendisk结构体来表示(在</linux/genhd.h>中定义),每个gendisk可以支持多个分区。

每个gendisk中包含了本物理实体的全部信息以及操作函数接口。整个块设备的注册过程是围绕gendisk来展开的。在驱动程序中需要初始化的gendisk的一些成员如下所示。

struct gendisk

{

int major;/* 主设备号 */

int first_minor; /* 第一个次设备号 */

int minors; /* 次设备号个数,一个块设备至少需要使用一个次设备号,而且块设

备的每个分区都需要一个次设备号,因此这个成员等于1,则表明该块

设备是不可被分区的,否则可以包含minors – 1 个分区。*/

char disk_name[32]; /* 块设备名称,在/proc/partions中显示 */

struct hd_struct **part; /* 分区表 */

struct block_device_operations *fops; /* 块设备操作接口,与字符设备的

file_operations结构对应*/

struct request_queue *queue; /* I/O请求队列 */

void *private_data; /* 指向驱动程序私有数据 */

sector_t capacity; /* 块设备可包含的扇区数 */

…… /* 其他省略 */

};

与字符设备驱动程序一样,块设备驱动程序也包含一个在<linux/fs.h>中定义的block_device_operations结构,其定义如下所示。

struct block_device_operations

{

int (*open) (struct inode *, struct file *);

int (*release) (struct inode *, struct file *);

int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);

long (*unlocked_ioctl) (struct file *, unsigned, unsigned long);

long (*compat_ioctl) (struct file *, unsigned, unsigned long);

int (*direct_access) (struct block_device *, sector_t, unsigned long *);

int (*media_changed) (struct gendisk *);

int (*revalidate_disk) (struct gendisk *);

int (*getgeo)(struct block_device *, struct hd_geometry *);

struct module *owner;

};

从该结构的定义中,可以看出块设备并不提供read()、write()等函数接口。对块设备的读写请求都是以异步方式发送到设备相关的request 队列之中。

3.块设备注册和初始化

块设备的初始化过程要比字符设备复杂,它既需要像字符设备一样在加载内核时完成一定的工作,还需要在内核编译时增加一些内容。块设备驱动程序初始化时,由驱动程序的init()完成。

块设备的初始化过程如图11.6所示。

图11.6 块设备驱动程序初始化过程

(1)向内核注册。

使用register_blkdev() 函数对设备进行注册。

int register_blkdev(unsigned int major, const char *name);

其中参数major为要注册的块设备的主设备号,如果其值等于0,则系统动态分配并返回主设备号。参数name为设备名,在/proc/devices中显示。如果出错,则该函数返回负值。

与其对应的块设备的注销函数为unregister_blkdev(),其格式如下所示。

int unregister_blkdev(unsigned int major, const char *name);

其参数必须与注册函数中的参数相同。如果出错则返回负值。

(2)申请并初始化请求队列。

这一步要调用blk_init_queue()函数来申请并初始化请求队列,其格式如下所示。

struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)

其中参数rfn是请求队列的处理函数指针,

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top