块设备驱动程序
块设备驱动程序时linux块子系统中的最底层组件,它们从IO调度程序中获得请求,然后按要求处理这些请求。每个块设备驱动程序是设备驱动程序模型的组成部分,因此,每个块设备驱动程序对应一个device_driver类型的描述符;此外,设备驱动程序处理的每个磁盘都与一个device描述符相关联。块IO子系统必须为系统中的每个块设备存放附加信息。一个块设备驱动程序可能处理几个块设备。
每个块设备都是由一个block_device结构的描述符来表示的
struct block_device{
dev_t bd_dev //块设备的主设备号和次设备号
struct inode* bd-inode //指向bdev文件系统中块设备对应的文件索引节点的指针
int bd_openers //计数器,统计块设备已经被打开了多少次
struct semaphore bd_sem //保护块设备的打开和关闭的信号量
struct semaphore bd_mount_sem //禁止在块设备上进行新安装的信号量
struct list_node bd_inodes //已打开的块设备文件的索引节点链表的首部
void * bd_holder //块设备描述符的当前索引者
int bd_holders //计数器,统计对bd_holder字段多次设置的次数
struct block_device * bd_contions //如果块设备是一个分区,则指向整个磁盘的块描述符;否则指向该块设备描述符块的大小
unsigned bd_block_size //块大小
struct hm_struct *bd_part //指向分区描述符的指针
unsigned bd_part_count //计数器,统计包含在块设备中的分区已经被打开了多少次
struct gendisk *bd_disk //指向块设备中基本磁盘的gendisk结构的指针
strcut list_head *bd_list //用于块设备描述符链表的指针
struct backing_dev_info *ing_Dev_info //指向块设备的专门描述符backing_dev_info的指针
unsigned long bd_private //指向块设备持有者的私有数据的指针
}
主,次设备号和相应的块设备描述符之间的关系是通过bdev特殊文件系统来维护的,每个块设备描述符都对应一个bdev特殊文件,块设备描述符的bd_inode字段指向相应的bdev索引节点,而该索引节点则将块设备的主次设备号和相应描述符的地址进行编码。
bdget()函数用来主次设备号来在文件系统中查找相关的索引节点。
注册和初始化设备驱动程序
自定义驱动程序描述符
设备驱动程序需要一个自定义的类型描述符,拥有驱动硬件设备所需的数据。一般包括使用的io端口,设备发出中断的IRQ线,设备的内部状态等
strcut foo_dev_t{
spinlock_t lock;
struct gendisk *gd;
}foo;
lock字段用来保护foo描述符中字段值的自旋锁;通常将其地址传给内核辅助函数,从而保护对驱动程序而言特定的块io子系统的数据结构,gd字段是指向gendisk描述符的指针。
预定主设备号
设备驱动程序必须自己预定一个主设备号,该操作通过register_blkdev()函数完成:
err=register_blkdev();
if(err) goto error_major_is_busy;
初始化自定义描述符
在使用驱动程序之前必须适当地初始化foo描述符中的所有字段。
spin_lock_init(&foo.lock)
foo.gd = alloc_disk(16);
if{!foo.gd} goto error_no_gendisk;
初始化gendisk描述符
初始化块设备操作表
分配和初始化请求队列
foo.gd->rq = blk_init_queue(foo_strategy, &foo.lock)
blk_init_queue()函数分配一个请求队列描述符并将其中许多字段初始化为缺省值。它接收的参数为设备描述符的自旋锁的地址和设备驱动程序的策略例程的地址,该函数也初始化foo.gd->rq->elevator字段,并强制驱动程序使用缺省的io调度算法。
设置中断处理程序
设备驱动程序需要为设备注册IRQ线
request_irq(foo.irq, foo_interrupt, SA_INTERRUPT|SA|SHIRQ, "foo", NULL);
注册磁盘
初始化阶段的最后一步就是注册和激活磁盘,可以通过执行下面的操作完成add_disk(fo.gd);
void add_disk(struct gendisk *disk)
{
disk->flags |= GENHD_FL_UP;
blk_register_region(MKDEV(disk->major, disk->first_minor),
disk->minors, NULL, exact_match, exact_lock, disk); //给设备分配主次设备号
register_disk(disk); //注册gendisk到通用块层
blk_register_queue(disk); //注册请求队列到通用块层
}
register_disk是这里面比较重要的函数,这个函数的分析可以看一下这个博主的博客
https://blog.csdn.net/ruanjianruanjianruan/article/details/53418301