MMC/SD设备驱动分为三个文件夹:host、card、core,这三个文件夹联系的非常紧密,初始化也好、扫描也好、读写也好,总是跳来跳去。
一 host的分配和添加
先看看初始化吧,host分配的代码位于driver/mmc/host/s3cmci.c。
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的分配和添加
这里就进入core的mmc_rescan()了。
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的读写
host和card都建立起来了,貌似也只是分配了结构;像个木头立在那一样的结构;这些结构如何和硬件的host和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设备
card是一个block设备,其读写都是block层的事情。读写之前需要把自己注册成为一个block,这里进入了card的block.c。
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,
};
mmc_blk_probe()做了哪些事情?
(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);
}
这里了drv->probe就是mmc_blk_probe。mmc_bus_probe()在哪里调用呢?card的block.c中mmc_register_driver(&mmc_driver)->driver_register(&drv->drv)->bus_add_driver(drv)->driver_attach()->__driver_attach()->driver_probe_device()->really_probe()->(dev->bus->probe(dev))。
__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;
}
mmc 子系统的driver和任何一个mmc的device都是可以match的,无论SDIO、MMC、SD,而不像i2c device,基本每个从设备都有自己的driver,要凭借id_table match。就是说每注册一个mmc driver或者每添加一个mmc device,都会执行mmc_bus_probe(),进而执行mmc_blk_probe()。
为什么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);
}
(mmc_queue)mq->queue->request_fn就是mmc_request_fn,queue->request_fn又是什么时候执行的?blk_queue_bio()->__blk_run_queue()->(q->request_fn(q))。(mmc_queue)mq->queue->make_request_fn就是blk_queue_bio,它又是哪里执行的呢?block/blk-core.c中generic_make_request()->(q->make_request_fn(q, bio))转发bio的时候用的,再往上找最好从read、write系统调用开始找起。能执行完这里就实现了card的读写,但其中涉及了复杂的IO调度算法。
还有前面为什么要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->queue
mmc_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;
}
找到的是(&(disk)->part0.__dev)->kobj;kobj找到device,由part0.__dev找到gendisk。
这是一个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;
}
blkdev_readpage()->block_read_full_page(page, blkdev_get_block)->get_block(inode, iblock, bh, 0)get_block就是blkdev_get_block,此时就得到了bh->b_bdev,block_read_full_page()里继续走submit_bh()->_submit_bh()->(bio->bi_bdev = bh->b_bdev),bio和bdev接头了。这个inode是struct bdev_inode结构中一个vfs_inode成员的指针,系统调用决定的;所以由inode,能找到bdev_inode;自然能找到struct block_device bdev。
struct bdev_inode {
struct block_device bdev;
struct inode vfs_inode;
};
分区和block device是怎么对应的:
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之后会分四种情况进行处理,也就是针对设备是不是第一次打开以及打开的设备是主设备还是分区来进行不同的处理。