1. 程式人生 > >linux驅動由淺入深系列:塊裝置驅動之三(塊裝置驅動結構分析,以mmc為例)

linux驅動由淺入深系列:塊裝置驅動之三(塊裝置驅動結構分析,以mmc為例)

linux驅動由淺入深系列:塊裝置驅動之一(高通eMMC分割槽例項)
前一篇文章介紹了塊裝置驅動在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成員)

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);建立的

其中呼叫了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函式發起讀寫請求,全文完。