首先,我们来看Makefile文件吧,Makefile中文件的目标文件的顺序是很重要的,因为这个会涉及到模块的依赖关系,比如说,如果这些源文件中有module_init(),则这些module_init就按在Makefile中的顺序链接进内核,之后也按照链接的顺序进行调用。根据我们的内核配置选项,将要编译进内核的文件就只有mmc.c,mmc_sysfs.c,mmc_block.c,mmc_queue.c,s3cmci.c这几个文件。其中mmc.c与mmc_queue.c主要是定义了一些其他文件中将要使用的函数,我们暂时不管它。接下来,我们来分析mmc_sysfs.c
我们先来看mmc_init(),这是系统启动后将要调用的,在mmc_init函数中,主要完成3项工作 :
workqueue = create_singlethread_workqueue("kmmcd");//创见一个单线程的工作队列
bus_register(&mmc_bus_type);//注册总线
class_register(&mmc_host_class);//注册mmc_host_class
mmc_bus_type的定义为:
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, .suspend = mmc_bus_suspend, .resume = mmc_bus_resume, }; |
这里,我们主要关注mmc_bus_match与mmc_bus_probe,此两函数将要在device_register和driver_register向总线注册设备的时候被调用。
static int mmc_bus_match(struct device *dev, struct device_driver *drv) { struct mmc_card *card = dev_to_mmc_card(dev); return !mmc_card_bad(card); } #define mmc_card_bad(c) ((c)->state&(MMC_STATE_BAD) |
可见,对于mmc总线,mmc_bus_match是通过返回struct mmc_card中的状态标示state位来实现的。
static int mmc_bus_probe(struct device *dev) { struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_card *card = dev_to_mmc_card(dev); return drv->probe(card); } |
在使用bus_register之后,我们可以在sysfs的/sys/bus目录里看到它
static struct class mmc_host_class = { .name = "mmc_host", .dev_release = mmc_host_classdev_release, }; class_register之后,在/sys/class目录下将出现mmc_host目录 |
---------------------------------------------------------------------------------
接下来,我们看mmc_block.c,还是从初始化函数mmc_blk_init开始分析
static int __init mmc_blk_init(void) { int res = -ENOMEM;
res = register_blkdev(major, "mmc"); if (res < 0) { printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n", major, res); goto out; } if (major == 0) major = res;
return mmc_register_driver(&mmc_driver);
out: return res; } |
在mmc_blk_init中, register_blkdev(major, "mmc")的作用是注册一个块设备。如果传递的major为0,这内核将分派一个新的主设备号给设备。
register_blkdev的功能比较少,一是动态分配设备号,二是在/proc/devices中创建一个入口项 。故通过它之后,系统还是不能使用块设备的。
接着我们看
return mmc_register_driver(&mmc_driver);
mmc_register_driver在mmc_sysfs.c中定义:
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, };
int mmc_register_driver(struct mmc_driver *drv) { drv->drv.bus = &mmc_bus_type; return driver_register(&drv->drv); } |
这两句代码比较好理解吧。在注册一个struct driver之前,都要先设置它的bus,类似的,在platform_driver_register中我们也可所以看到:
drv->driver.bus=&platform_bus_type
driver_register将到相应总线mmc_bus_type上去搜索相应设备。找到设备后就设置dev->driver=drv,并调用mmc_bus_type总线的probe函数被调用,即mmmc_bus_probe函数.
我们跟踪driver_register(&drv->drv),它会调应bus_add_driver。
int bus_add_driver(struct device_driver *drv) { ... ... error = kobject_set_name(&drv->kobj, "%s", drv->name); if (error) goto out_put_bus; drv->kobj.kset = &bus->drivers; if ((error = kobject_register(&drv->kobj))) goto out_put_bus;
error = driver_attach(drv); ... ... } |
在调用kobject_register之后,我们可以在/sys/bus/mmc/driver目录下看到mmcblk文件driver_attach函数会遍历相应总线(mmc_bus_type)上的dev,对这些dev执行总线的match函数(mmc_bus_match)。如果match成功,它会调用mmc_bus_type总线的probe函数mmc_bus_probe(如果总线的probe存在的话).我们在以上走过的流程中可以看到,我们并没有向总线添加任何设备,故mmc_bus_probe是不会调用的。但相应的driver已经注册到系统了。
那么,现在mmc_block.c中的分析就先暂时到此为止。。。不过,等会儿我们还会继续回到这个文件的。。
我们接着来到s3cmci.c文件
s3cmci_init----->platform_driver_register(&s3cmci_driver_2440)------------>s3cmci_probe_2440----->s3cmci_probe
在s3cmci_probe中主要是分配及初始化
struct mmc_host *mmc;
struct s3cmci_host *host;
这两个结构体。分配DMA通道,注册irq中断。
以下对个别函数的作用进行说明:
1: clk_get
系统初始化的时候外围总线上的设备不是都给时钟的,主要是为了省电。
在2.6.20中,我们在文件arch/arm/mach-s3c2410/s3c2410-clock中可以看到nand,sdi,adc,i2c,iis这些是不给时钟的
lcd,gpio,usb,uart这些是给时钟的
clk_get在arch/arm/mach-s3c2410/clock.c中定义
2: mmc_alloc_host
mmc_alloc_host分配sizeof(struct mmc_host)+extra这么大的空间,并做以下初始化
host = mmc_alloc_host_sysfs(extra, dev);
host->parent = dev;
host->class_dev.parent = dev;
host->class_dev.class = &mmc_host_class;
device_initialize(&host->class_dev);
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
INIT_LIST_HEAD(&host->cards);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
* By default, hosts do not support SGIO or large requests.
* They have to set these according to their abilities.
host->max_hw_segs = 1;
host->max_phys_segs = 1;
host->max_sectors = 1 << (PAGE_CACHE_SHIFT - 9);
host->max_seg_size = PAGE_CACHE_SIZE;
3:
mmc_add_host
调用mmc_add_host的最终结果是device_add(&host->class->dev)(所以在/sys/class/mmc/目录下出现mmc0文件)。
并会调用mmc_power_off(host);mmc_detect_change(host, 0);
mmc_power_off函数比较简单,顾名思义,它是让SD/MMC卡停止工作的,相应的,此函数就会配置相应的IO口以及时钟等。
我们来看看mmc_detect_change函数吧。
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
mmc_schedule_delayed_work(&host->detect, delay);
}
int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay)
{
return queue_delayed_work(workqueue, work, delay);
}
queue_delayed_work把host->detect提交到workqueue工作对列中。
相应定义:
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
workqueue = create_singlethread_workqueue("kmmcd");
所以,当此delayed_work执行的时候,mmc_rescan将会被调用
static void mmc_rescan(struct work_struct *work) { struct mmc_host *host = container_of(work, struct mmc_host, detect.work);//返回指向s3cmci_probe中分配的mmc_host结构的指针
struct list_head *l, *n; unsigned char power_mode;
mmc_claim_host(host); /* 驱动中使用mmc_claim_host(host);来得知,当前mmc控制器是否被占用,当前mmc控制器如果被占用,那么 host->claimed = 1;否则为0,如果为1,那么会在for(;;)循环中调用schedule切换出自己,当占用mmc控制器的操作完成之后,执行 mmc_release_host()的时候,会激活登记到等待队列&host->wq中的其他程序获得mmc主控制器的物理使用权 */ /* * Check for removed cards and newly inserted ones. We check for * removed cards first so we can intelligently re-select the VDD. */ power_mode = host->ios.power_mode; if (power_mode == MMC_POWER_ON) mmc_check_cards(host); mmc_setup(host); /* * Some broken cards process CMD1 even in stand-by state. There is * no reply, but an ILLEGAL_COMMAND error is cached and returned * after next command. We poll for card status here to clear any * possibly pending error. */ if (power_mode == MMC_POWER_ON) mmc_check_cards(host);
if (!list_empty(&host->cards)) { /* * (Re-)calculate the fastest clock rate which the * attached cards and the host support. */ host->ios.clock = mmc_calculate_clock(host); mmc_set_ios(host); }
mmc_release_host(host);
list_for_each_safe(l, n, &host->cards) { struct mmc_card *card = mmc_list_to_card(l);
/* * If this is a new and good card, register it. */ if (!mmc_card_present(card) && !mmc_card_dead(card)) { if (mmc_register_card(card)) mmc_card_set_dead(card); else mmc_card_set_present(card); }
/*s * If this card is dead, destroy it. */ if (mmc_card_dead(card)) { list_del(&card->node); mmc_remove_card(card); } }
/* * If we discover that there are no cards on the * bus, turn off the clock and power down. */ if (list_empty(&host->cards)) mmc_power_off(host); }
|
起初,power_mode = MMC_POWER_OFF,故第一个mmc_check_cards(host)是不会执行的。但mmc_setup函数中设置了power_mode = MMC_POWER_ON。故第二个mmc_check_cards(host)是会执行的。
在mmc_setup中,会调用mmc_discover_cards.如果发现有卡,则add new card to list.
因为现在没有发现卡,host->card=NULL,故mmc_check_cards与mmc_rescan中的list_for_each_safe循环体中的内容也是不会执行的。
static void mmc_check_cards(struct mmc_host *host) { struct list_head *l, *n;
mmc_deselect_cards(host);// Ensure that no card is selected.
list_for_each_safe(l, n, &host->cards) { struct mmc_card *card = mmc_list_to_card(l); struct mmc_command cmd; int err;
cmd.opcode = MMC_SEND_STATUS; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); if (err == MMC_ERR_NONE) continue;
mmc_card_set_dead(card); } } |
执行完s3cmci_probe后,在终端会有以下信息:
s3c2410-sdi s3c2410-sdi: powered down.
s3c2410-sdi s3c2410-sdi: initialisation done.
s3c2410-sdi s3c2410-sdi: running at 0kHz (requested: 0kHz).
s3c2410-sdi s3c2410-sdi: running at 198kHz (requested: 197kHz).
s3c2410-sdi s3c2410-sdi: running at 198kHz (requested: 197kHz).
s3c2410-sdi s3c2410-sdi: running at 198kHz (requested: 197kHz).
s3c2410-sdi s3c2410-sdi: CMD[TIMEOUT] #2 op:APP_CMD(55) arg:0x00000000 flags:0xe
s3c2410-sdi s3c2410-sdi: CMD[TIMEOUT] #3 op:APP_CMD(55) arg:0x00000000 flags:0xe
s3c2410-sdi s3c2410-sdi: CMD[TIMEOUT] #4 op:APP_CMD(55) arg:0x00000000 flags:0xe
s3c2410-sdi s3c2410-sdi: CMD[TIMEOUT] #5 op:APP_CMD(55) arg:0x00000000 flags:0xe
s3c2410-sdi s3c2410-sdi: CMD[TIMEOUT] #6 op:ALL_SEND_OCR(1) arg:0x00000000 flage
s3c2410-sdi s3c2410-sdi: powered down.
至此,我们已经跟踪了mmc/sd卡驱动的注册。。我们接着来看插入拔除卡的中断处理函数:
static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id) { struct s3cmci_host *host = (struct s3cmci_host *)dev_id;
dbg(host, dbg_irq, "card detect\n");
mmc_detect_change(host->mmc, 500);
return IRQ_HANDLED; } |
可见,这里也会调用mmc_detect_change。。。我们跟着前面的分析来到mmc_setup这里,此时mmc_setup调用mmc_discover_cards。Create a mmc_card entry for each discovered card,add new card to list.同时还会调用mmc_read_switch_caps或者mmc_process_ext_csds来实现对大容量卡的支持(>4G)
跟着程序的流程我们来到
if (!mmc_card_present(card) && !mmc_card_dead(card)) {
if (mmc_register_card(card))
来看
int mmc_register_card(struct mmc_card *card) { int ret;
snprintf(card->dev.bus_id, sizeof(card->dev.bus_id), "%s:%04x", mmc_hostname(card->host), card->rca);
ret = device_add(&card->dev); if (ret == 0) { if (mmc_card_sd(card)) { ret = device_create_file(&card->dev, &mmc_dev_attr_scr); if (ret) device_del(&card->dev); } } return ret; } |
此函数在mmc_sysfs.c中定义。 device_add(&card->dev)将到相应总线mmc_bus_type上去搜索相应驱动。找到驱动后就设置dev->driver=drv,并调用mmc_bus_type总线的probe函数被调用,即mmmc_bus_probe函数
static int mmc_bus_probe(struct device *dev) { struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_card *card = dev_to_mmc_card(dev);
return drv->probe(card); }
|
mmc_bus_probe会调用mmc_blk_probe
mmc_blk_probe()首先分配一个新的mmc_blk_data结构变量,然后调用mmc_init_queue,初始化blk队列。然后建立一个线程mmc_queue_thread()。
static int mmc_blk_probe(struct mmc_card *card) { struct mmc_blk_data *md; int err;
/* * Check that the card supports the command class(es) we need. */ if (!(card->csd.cmdclass & CCC_BLOCK_READ)) return -ENODEV;
md = mmc_blk_alloc(card);//
if (IS_ERR(md)) return PTR_ERR(md);
err = mmc_blk_set_blksize(md, card); if (err) goto out;
printk(KERN_INFO "%s: %s %s %lluKiB %s\n", md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), (unsigned long long)(get_capacity(md->disk) >> 1), md->read_only ? "(ro)" : "");
mmc_set_drvdata(card, md); add_disk(md->disk); return 0;
out: mmc_blk_put(md);
return err; } |
struct mmc_blk_data封装了struct gendisk 与 struct mmc_queue,而struct mmc_queue封装了struct mmc_card与struct request。
static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) { struct mmc_blk_data *md; int devidx, ret;
devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS); if (devidx >= MMC_NUM_MINORS) return ERR_PTR(-ENOSPC); __set_bit(devidx, dev_use);
md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); if (!md) { ret = -ENOMEM; goto out; }
memset(md, 0, sizeof(struct mmc_blk_data));
/* * Set the read-only status based on the supported commands * and the write protect switch. */ md->read_only = mmc_blk_readonly(card);//写保护
/* * Both SD and MMC specifications state (although a bit * unclearly in the MMC case) that a block size of 512 * bytes must always be supported by the card. */ md->block_bits = 9;//块大小
md->disk = alloc_disk(1 << MMC_SHIFT); //分配struct gendist,驱动程序不能自己动态分配该结构,而是必须调用
//alloc_disk,其参数为次设备号数目,注意了,是次设备号数目,不是次设备号
if (md->disk == NULL) { ret = -ENOMEM; goto err_kfree; }
spin_lock_init(&md->lock); md->usage = 1;
ret = mmc_init_queue(&md->queue, card, &md->lock);//
if (ret) goto err_putdisk;
md->queue.prep_fn = mmc_blk_prep_rq; md->queue.issue_fn = mmc_blk_issue_rq; md->queue.data = md; //上面设置了请求队列,现在就可以初始化及安装相应的gendisk结构了
md->disk->major = major; md->disk->first_minor = devidx << MMC_SHIFT; md->disk->fops = &mmc_bdops; md->disk->private_data = md; md->disk->queue = md->queue.queue; md->disk->driverfs_dev = &card->dev;
/* * As discussed on lkml, GENHD_FL_REMOVABLE should: * * - be set for removable media with permanent block devices * - be unset for removable block devices with permanent media * * Since MMC block devices clearly fall under the second * case, we do not set GENHD_FL_REMOVABLE. Userspace * should use the block device creation/destruction hotplug * messages to tell when the card is present. */
sprintf(md->disk->disk_name, "mmcblk%d", devidx);
blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits);
/* * The CSD capacity field is in units of read_blkbits. * set_capacity takes units of 512 bytes. */ set_capacity(md->disk, card->csd.capacity << (card->csd.read_blkbits - 9)); return md;
err_putdisk: put_disk(md->disk); err_kfree: kfree(md); out: return ERR_PTR(ret); }
|
至此,驱动向系统添加了一个块设备。
请求处理过程:
mmc_request--->mmc_queue_thread----->mmc_blk_issue_rq---->mmc_wait_for_req--->mmc_start_request---->s3cmci_request