linux驱动由浅入深系列:块设备驱动之三(块设备驱动结构分析,以mmc为例)

linux驱动由浅入深系列:块设备驱动之一(高通eMMC分区实例)

linux驱动由浅入深系列:块设备驱动之二(从用户空间的read、write到实际设备物理操作整体架构分析)
linux驱动由浅入深系列:块设备驱动之三(块设备驱动结构分析,以mmc为例)

前一篇文章介绍了块设备驱动在linux框架张的位置关系,本文来分析一下驱动本身。
块设备驱动的模型还是基本基于字符设备驱动的,可以简单理解为块设备仅仅增加了操作缓冲区,对用户操作请求进行队列重排。因此只在有了字符驱动框架的基础上,本文重点介绍块设备独有的队列操作相关函数。
重要结构体
struct gendisk是块设备通用结构体,定义如下:
linux-3.16.56 / include / linux / genhd.h

相关函数

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

创建一个块设备,当major==0时,表示动态创建,创建成功会返回一个主设备号

2,struct gendisk *alloc_disk(int minors);

分配一个gendisk结构,minors为分区数,填1表示不分区

3,request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock);
分配一个request_queue请求队列,分配成功返回一个request_queue结构体
  rfn: request_fn_proc结构体,用来执行放置在队列中的请求的处理函数

  lock:队列访问权限的自旋锁(spinlock),该锁通过DEFINE_SPINLOCK()来定义

4,static DEFINE_SPINLOCK(spinlock_t lock);     

定义一个自旋锁(spinlock)

5,static inline void set_capacity(struct gendisk *disk, sector_t size);

设置gendisk结构体的扇区数

6,void add_disk(struct gendisk *gd);

向内核中注册gendisk结构体

7,struct request *blk_fetch_request(request_queue_t *q);

获取申请队列中未完成的申请,获取成功返回一个request结构体,不成功返回NULL
linux2.6内核中该函数为 elv_next_request()

mmc块设备驱动分析

1,设备的定义一般位于DTS文件中,开机会遍历DTS树,注册设备,这部分就不展示了,DTS相关内容欢迎查看本博客文章:

Linux DTS(Device Tree Source)设备树详解之一
我们从probe函数开始分析:
linux-3.16.56/drivers/mmc/card/block.c

a,其中mmc_blk_alloc_parts()函数会分配gendisk结构体,创建请求队列等
b,之后mmc_add_disk()中会调用add_disk(md->disk);(文章开头介绍的常用函数之一)向内核注册块设备。

2,mmc_blk_alloc_parts()函数定义在:
linux-3.16.56 / drivers / mmc / card / block.c

其中遍历磁盘上的分区,逐一调用mmc_blk_alloc_part()分配gendisk结构体。(mmc_blk_data中含有gendisk成员)

3,mmc_blk_alloc_part()函数定义在:
linux-3.16.56/drivers/mmc/card/block.c

4,实际分配空间的函数在mmc_blk_alloc_req(),这是比较核心的一个初始化函数,定义在:
linux-3.16.56/drivers/mmc/card/block.c


其中md->disk = alloc_disk(perdev_minors);(文章开头介绍的常用函数之一)分配了gendisk块设备通用结构体,然后填充了相关信息,比较重要的有:
a,md->disk->major = MMC_BLOCK_MAJOR  mmc设备定义的主设备号为179.
b,md->disk->queue = md->queue.queue;赋值请求队列。该请求队列是又上面

        ret = mmc_init_queue(&md->queue, card, &md->lock, subname);创建的

5,mmc_init_queue()函数定义在:
linux-3.16.56/drivers/mmc/card/queue.c

其中调用了blk_init_queue()(文章开头介绍的常用函数之一)初始化一个块设备驱动的请求队列。

6,blk_init_queue的第一个参数mmc_request_fn就是mmc驱动的队列处理函数,定义为:
linux-3.16.56/drivers/mmc/card/queue.c

其中blk_fetch_request()(文章开头介绍的常用函数之一)会取出队列中的请求,之后调度mmc驱动中的队列请求处理线程:wake_up_process(mq->thread)。

7,mq->thread的赋值在:
linux-3.16.56 / drivers / mmc / card / queue.c

mmc_queue_thread()线程体函数的定义在
linux-3.16.56 / drivers / mmc / card / queue.c

其中对队列中的请求一通折腾先忽略,最终是通过调用mq->issue_fn(mq, req);去做实际的mmc读写操作的。
8,mq->issue_fn函数指针的赋值在:
linux-3.16.56 / drivers / mmc / card / block.c

其中mmc_blk_issue_rq()函数的定义在:
linux-3.16.56 / drivers / mmc / card / block.c

其中会调用mmc_blk_issue_rw_rq()函数向物理mmc设备发起读写请求。
9, mmc_blk_issue_rw_rq()函数定义在:
linux-3.16.56/drivers/mmc/card/block.c

其中调用mmc_start_req()发起读写请求,函数定义在:

其中__mmc_start_data_req()定义在:

其中mmc_start_request()定义在:
linux-3.16.56/drivers/mmc/core/core.c

mmc_start_request()函数最后会调用相应mmc host注册的request函数发起读写请求,全文完。

猜你喜欢

转载自blog.csdn.net/RadianceBlau/article/details/80592343
今日推荐