轉自:http://blog.csdn.net/coldsnow33/article/details/13676883
MMC/SD設備驅動分爲三個文件夾:host、card、core,這三個文件夾聯繫的非常緊密,初始化也好、掃描也好、讀寫也好,總是跳來跳去。
一 host的分配和添加
s3cmci_probe()中的重要函數
mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
1 host->rescan_disable = 1;失能card檢測。
2 給host分配index,類似ID;
(1) 分配idr的後備資源,預備役。idr_pre_get(&mmc_host_idr, GFP_KERNEL);
(2) get一個與host相關聯的id到&host->index。err = idr_get_new(&mmc_host_idr, host, &host->index);
3 初始化等待隊列頭。init_waitqueue_head(&host->wq);
4 初始化延時工作隊列工作。INIT_DELAYED_WORK(&host->detect, mmc_rescan);
(1) (_work)->func = (_func);
(2) 初始化&(_work)->timer定時器,到期的回調函數爲delayed_work_timer_fn。
- void delayed_work_timer_fn(unsigned long __data)
- {
- struct delayed_work *dwork = (struct delayed_work *)__data;
- struct cpu_workqueue_struct *cwq = get_work_cwq(&dwork->work);
- /* should have been called from irqsafe timer with irq already off */
- __queue_work(dwork->cpu, cwq->wq, &dwork->work);
- }
ret = mmc_add_host(mmc);
ret = mmc_add_host(mmc);mmc_start_host(host);
mmc_detect_change(host, 0);
mmc_schedule_delayed_work(&host->detect, delay);
- static struct workqueue_struct *workqueue;
- static int mmc_schedule_delayed_work(struct delayed_work *work,
- unsigned long delay)
- {
- return queue_delayed_work(workqueue, work, delay);
- }
調度一個延時工作項,就是加入到workqueue,最後會insert到線程池worker_pool的worklist上;delay == 0馬上加入,否則,add timer,等到定時到期的回調函數中會執行相同的功能。
mmc_power_off(host);名爲斷電;包括一些io設置等。
這些工作做完,mmc_rescan()就開始運行了,它是用來掃描card的。
二 card的分配和添加
void mmc_rescan(struct work_struct *work)
1 if (host->rescan_disable)就直接return了,所以一定要使能card檢測。
2 host->bus_ops是個什麼東西?第一次rescan時,它是NULL的。
3 請求一個host控制器。
mmc_claim_host(host);
__mmc_claim_host(host, NULL);
(1) 初始化話了一個等待隊列wait_queue_t,添加到host->wq的等待隊列頭上。
(2) 設置當前進程是不可中斷的。
(3) 查看是否滿足終止、host可用、擁有host的是當前線程,滿足則break;否則,沒有獲得host控制器的使用權,就schedule()調度出去。
(4) 設置當前進程爲運行狀態。
(5) 請求到控制器後,如果只有一張卡,stop會一直爲0,沒有衝突;那麼設置一些請求標記;否則,stop爲1,就wake up hots->wq上的等待隊列;馬上把這個等待隊列wait從wq中remove。看來是要wake up其他的wait;爲什麼不先remove本wait,再wake up其他的?
(6) 如果沒有停止,enble host。
(7) stop代表終止host的等待隊列並喚醒。
4 mmc_rescan_try_freq(host, max(freqs[i], host->f_min)。
(1) power up
(2) 硬件reset,一是host->ops->hw_reset(host);2是發送CMD52 reset sdio,sd/mmc忽略此命令。
(3) CMD0,設置卡爲idle state,注意,如果DAT3/nCS是低電平,就進入SPI模式了。
(4) CMD8,ACM41之前一定會發送這個cmd,SD2.0會響應;SD1.0會fail,區分出來後爲什麼沒有保存?
CMD8的參數:cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;spec2.0:
spec3.0有改動
注:Order's important: probe SDIO, then SD, then MMC,接下來會區分SDIO、SD、MMC
(5) mmc_attach_sdio(host),響應CMD5,匹配SDIO的電壓,調用mmc_sdio_init_card()。
(6) mmc_attach_sd(host),響應ACMD41,匹配SD卡電壓,是否支持1.8V UHS-I mode;調用mmc_sd_init_card()。
(7) mmc_attach_mmc(host),響應CMD1,同樣是匹配電壓,調用mmc_init_card()。
注:若匹配電壓的cmd,發送的arg是0,則卡僅傳回OCR的值,不判斷;若arg電壓參數存在,則和卡的OCR對不,若不符合,卡進入inactive state;符合,返回OCR寄存器的值,卡進入ready state。
(8) mmc_attach_xx()裏總會調用mmc_attach_bus(host, bus_ops);host->bus_ops = ops;就是前面說到的,賦值一個bus的操作函數集。
(9) 以SD爲例,看一看mmc_sd_init_card()的過程,CMD2-CID identification state(ident),CMD3-RCA stand-by state(stby),CMD9-CSD,CMD7-RCA,ACMD51-SCR;若支持UHS超高速則初始化超高速,否則,就切換到高速;將clock提高。這些切換都是CMD6做的事情。
(10) CMD2成功後,會調用mmc_alloc_card();
malloc了一個mmc_card,不要忘記有err的時候free這個結構,所以card->dev.release = mmc_release_card;device.release(),當指向設備的最後一個引用被刪除時,內核調用該方法; 它將從內嵌的kobject的release方法中調用,這個應該在設備模型中,認真地說一說。當出錯的時候,跳到最後判斷一下,是否剛剛分配了一個card;如果是,那麼mmc_remove_card(card);。
- void mmc_remove_card(struct mmc_card *card)
- {
- #ifdef CONFIG_DEBUG_FS
- mmc_remove_card_debugfs(card);
- #endif
- if (mmc_card_present(card)) {
- if (mmc_host_is_spi(card->host)) {
- pr_info("%s: SPI card removed\n",
- mmc_hostname(card->host));
- } else {
- pr_info("%s: card %04x removed\n",
- mmc_hostname(card->host), card->rca);
- }
- device_del(&card->dev);
- }
- put_device(&card->dev);
- }
- #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
該宏判斷card是否存在,如果存在說明已經add card->dev了,就需要del card->dev;不存在就跳過好了。mmc_alloc_card()並沒有add card->dev,什麼時候add?原來是mmc_attach_sd()->mmc_add_card()->device_add(&card->dev)添加的,同時設置存在標誌mmc_card_set_present(card);。
mmc_host結構中有mmc_card,該card需要依附這個mmc_host;mmc_card結構中有mmc_host,該host是mmc_card應該屬於的host。mmc_sd_init_card()-> mmc_alloc_card(host, &sd_type)->(card->host = host;)alloc card的時候,會給card賦予它該屬於的host;mmc_sd_init_card()的最後->(host->card = card;)也會個host賦予依附它的card。
mmc_rescan()是host->detect,延時工作項的工作函數;如果要實時地掃描到卡,這個work就要不停的工作,所以mmc_rescan()的最後判斷了host->caps & MMC_CAP_NEEDS_POLL,就會調度該work了:mmc_schedule_delayed_work(&host->detect, HZ);。
mmc_init_card() //cmd2-CID,cmd3-RCA,cmd9-CSD,cmd7-RCA, cmd7-EXT_CSD。
三 card的讀寫
mmc_host結構中的const struct mmc_host_ops *ops;是一個函數操作集,s3cmci_probe()->(mmc->ops = &s3cmci_ops;)。這個集子裏的很多函數在初始化card的時候已經用過,set_ios用於io設置,比如clk;get_ro確定card是否寫保護,就是read-only;get_cd是檢測card是否absent;這些函數都比較簡單,基本設置一下寄存器或者讀取一下探測引腳就搞定了;還有一個複雜點的request,它就是card的讀寫函數。忽略錯誤情況,看看讀寫的調用關係:s3cmci_request()->s3cmci_send_request()->區分讀寫,區分dma、pio就行了。這個request誰用它?什麼時候用它?
card註冊爲block設備
static int __init mmc_blk_init(void)
1 註冊block設備,主設備號179。register_blkdev(MMC_BLOCK_MAJOR, "mmc")。
2 註冊mmc_driver。mmc_register_driver(&mmc_driver);。
(1) mmc_driver結構
- static struct mmc_driver mmc_driver = {
- .drv = {
- .name = "mmcblk",
- },
- .probe = mmc_blk_probe,
- .remove = mmc_blk_remove,
- .suspend = mmc_blk_suspend,
- .resume = mmc_blk_resume,
- };
(a) mmc_blk_alloc()->mmc_blk_alloc_req()->
alloc_disk(perdev_minors);
ret = mmc_init_queue(&md->queue, card, &md->lock, subname);
md->queue.issue_fn = mmc_blk_issue_rq;
md->disk->private_data = md;
md->disk->queue = md->queue.queue;
(b) mmc_blk_alloc_parts(card, md)只是針對mmc的boot0和boot1分區的,添加到&md->part list上,list_for_each_entry(part_md, &md->part, part)就遍歷到了。
(c) mmc_add_disk(md)->add_disk(md->disk);
總之,mmc_blk_probe()執行後,disk就出來了,mmc_blk_probe什麼時候調用?
(3) mmc_bus_type結構
mmc_register_driver()->(drv->drv.bus = &mmc_bus_type;)。mmc_bus_type是core的bus.c中的。
- static struct bus_type mmc_bus_type = {
- .name = "mmc",
- .dev_attrs = mmc_dev_attrs,
- .match = mmc_bus_match,
- .uevent = mmc_bus_uevent,
- .probe = mmc_bus_probe,
- .remove = mmc_bus_remove,
- .pm = &mmc_bus_pm_ops,
- };
- static int mmc_bus_probe(struct device *dev)
- {
- struct mmc_driver *drv = to_mmc_driver(dev->driver);
- struct mmc_card *card = mmc_dev_to_card(dev);
- return drv->probe(card);
- }
__driver_attach()->driver_match_device(drv, dev)->(drv->bus->match(dev, drv))
需要用到mmc bus的match函數:
- static int mmc_bus_match(struct device *dev, struct device_driver *drv)
- {
- return 1;
- }
爲什麼drv->probe(card)就是mmc_blk_probe()?mmc drvier就一份,遍歷所有的mmc device,只要存在都會和這個driver match,或者device剛剛添加也會和這個driver match,自然 mmc_driver也就可以找到,mmc_driver .probe就是mmc_blk_probe。mmc device的創建:mmc_rescan()->mmc_rescan_try_freq()->mmc_attach_sd()->mmc_sd_init_card()->mmc_alloc_card()->(card->dev.bus = &mmc_bus_type);add device的時候,要device_attach,就會執行上面說的一堆東西。mmc device的添加:mmc_attach_sd()->mmc_add_card(host->card)->device_add(&card->dev)。
mmc bus註冊,仍然是core中core.c, subsys_initcall(mmc_init);,子系統級別。mmc_init()->mmc_register_bus()->bus_register(&mmc_bus_type),註冊子系統,註冊一個mmc總線。
request運行
mmc_blk_probe做了很多事情
1 mmc_blk_alloc()->mmc_blk_alloc_req()->(1) mmc_init_queue(&md->queue, card, &md->lock, subname)初始化一個mmc_queue結構。
mq->queue = blk_init_queue(mmc_request_fn, lock);初始化一個request_queue結構
blk_init_queue_node(rfn, lock, NUMA_NO_NODE);
blk_init_allocated_queue(uninit_q, rfn, lock);
q->request_fn = rfn;
blk_queue_make_request(q, blk_queue_bio);
q->make_request_fn = mfn;
(mmc_queue)mq->queue->request_fn就是mmc_request_fn,(mmc_queue)mq->queue->make_request_fn就是blk_queue_bio。
mmc_init_queue(&md->queue, card, &md->lock, subname)
mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s", host->index, subname ? subname : "");run了這個線程。
(2) md->queue.issue_fn = mmc_blk_issue_rq;
(3) md->disk->queue = md->queue.queue;
mmc_queue_thread線程做了什麼事情?
假設這裏是天堂,想讓什麼事情發生都可以,我想執行到mq->issue_fn(mq, req)。mq就是mmc_queue_thread傳進來的參數;struct request_queue *q = mq->queue,req = blk_fetch_request(q),req是由mq->queue fetch出來的request,就是從mq->queue->queue_head上摘下來一個request,獲取隊列中第一個未完成的請求。mq->issue_fn = mmc_blk_issue_rq。
mmc_blk_issue_rq()->mmc_blk_issue_rw_rq()->mmc_wait_for_req()->__mmc_start_req()->mmc_start_request()->(host->ops->request(host, mrq));mmc_wait_for_req_done()會等待req done,否則retries幾次。沒有讀寫的時候,該線程是會休眠的,誰喚醒它?
- static void mmc_request_fn(struct request_queue *q)
- {
- struct mmc_queue *mq = q->queuedata;
- struct request *req;
- if (!mq) {
- while ((req = blk_fetch_request(q)) != NULL) {
- req->cmd_flags |= REQ_QUIET;
- __blk_end_request_all(req, -EIO);
- }
- return;
- }
- if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
- wake_up_process(mq->thread);
- }
還有前面爲什麼要fetch request?mq->queue->queue_head上掛了很多request嗎?什麼時候掛上的?
blk_queue_bio()->add_acct_request()->__elv_add_request()->ELEVATOR_INSERT_FRONT(list_add(&rq->queuelist, &q->queue_head))
blk_queue_bio()->add_acct_request()->__elv_add_request()->ELEVATOR_INSERT_BACK(list_add_tail(&rq->queuelist, &q->queue_head))
blk_queue_bio()->add_acct_request()->__elv_add_request()->blk_insert_flush()->blk_flush_complete_seq()->(list_add(&rq->queuelist, &q->queue_head))
blk_flush_complete_seq()->blk_kick_flush()->(list_add_tail(&q->flush_rq.queuelist, &q->queue_head))
blk_queue_bio()中先add_acct_request(q, req, where),然後__blk_run_queue(q)。就是先add request,這request也許是已經合併的request;如果可以合併,那麼將新的 bio 請求掛載到一個已經存在 request 中;如果不能合併,那麼分配一個新的 request,然後將 bio 添加到其中。struct request來表示等待處理的塊設備請求,是bio提交給IO調度器產生的數據。__blk_run_queue(q)執行mmc_request_fn去wake up那個mmc_queue_thread線程,線程就會調用s3cmci_ops.request()實現讀寫了。
generic_make_request()是怎麼找到對應的request_queue的?
bio->bi_bdev->bd_disk->queuemmc_blk_probe()->mmc_blk_alloc(card)->mmc_blk_alloc_req()->(md->disk->queue = md->queue.queue;)這裏disk->queue已經出來了。
mmc_blk_probe()->mmc_add_disk(md)->add_disk(md->disk)->register_disk(disk)
1 bdget_disk()是說partno爲0,表示的是整個card的分區part0,通常來說,一塊有4個分區的磁盤,應該對應1個gendisk、4個hd_struct和5個block_device。根據這個分區的dev_t創建一個block_device,也就是說part0和block_device是靠設備號聯繫的。繼續向下看。
這個分區表part_tbl是怎麼來的?
alloc_disk(perdev_minors)->alloc_disk_node(minors, NUMA_NO_NODE)
(1) init_part_stats(&disk->part0)
(2) disk_expand_part_tbl(disk, 0)->(target = partno + 1, new_ptbl->len = target;)
(3) disk->part_tbl->part[0] = &disk->part0;
2 blkdev_get()->__blkdev_get()
(1) disk = get_gendisk(bdev->bd_dev, &partno);
kobj_lookup()->probe(dev, index, data)找到kobj。
add_disk()->blk_register_region(disk_devt(disk), disk->minors, NULL, exact_match, exact_lock, disk)->kobj_map()->(p->get = probe);probe就是exact_match:
- static struct kobject *exact_match(dev_t devt, int *partno, void *data)
- {
- struct gendisk *p = data;
- return &disk_to_dev(p)->kobj;
- }
這是一個for循環,根據bdev->bd_dev塊設備號,在bdev_map中查找bdev內嵌的kobject結構,實際上是先找到*data就是要找的disk;繞回去又繞回來。爲什麼能找到disk?kobj_map()裏p->data = data就這樣添加的。
(2) bdev->bd_disk = disk;到這裏bdev和disk聯繫上了,剩下就是bio和bdev怎麼接頭的?
- static int
- blkdev_get_block(struct inode *inode, sector_t iblock,
- struct buffer_head *bh, int create)
- {
- bh->b_bdev = I_BDEV(inode);
- bh->b_blocknr = iblock;
- set_buffer_mapped(bh);
- return 0;
- }
- struct bdev_inode {
- struct block_device bdev;
- struct inode vfs_inode;
- };
add_disk()->blk_register_region()->kobj_map()相當於註冊了n個block device。
add_disk()->register_disk()->blkdev_get()->__blkdev_get()->rescan_partitions()->add_partition()會掃描分區,確定(&((part)->__dev))->devt。
blkdev_open()會獲取block_device的實例,如果不能直接找到b_dev,此時會創建一個b_dev。獲取到gendisk之後會分四種情況進行處理,也就是針對設備是不是第一次打開以及打開的設備是主設備還是分區來進行不同的處理。