本文共 5048 字,大约阅读时间需要 16 分钟。
1 确定关注范围
根据公司代码编译出的原始.o 文件,可以间接确定真正起作用的源文件包括(也可以通过 Makefile 来直接确定)。
1) <kernel/driver/mmc/card>
card目录下的驱动文件是卡的设备驱动,也就是针对mmc或者sd卡的块设备驱动
block.c queue.c mmc_test.c sdio_card.c
<block.c>
static int __init mmc_blk_init(void){... res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");... res = mmc_register_driver(&mmc_driver);...}
register_blkdev为注册一个块设备,mmc_register_driver为注册一个mmc驱动。mmc_driver中最重要的函数:mmc_blk_probe,用于找到匹配的mmc_card。
<card.h>
struct mmc_card { struct mmc_host *host;...}
host 指针指向一个 mmc 主机实例,块设备中的读写操作就是调用这个mmc主机的操作函数host->ops->request来实现对实际硬件的操作。要找到这mmc_card,就得先把mmc_card这个设备挂载到mmc_bus去。mmc_bus 的注册在第二部分实现,mmc_card的挂载在第三部分实现。
2) <kernel/driver/mmc/core>
core 目录下的驱动文件是 mmc 总线驱动程序
core.c host.c mmc.c quirks.c sdio_cis.c sdio_irq.c sdio_ops.c sd_ops.c bus.c debugfs.c mmc_ops.c sdio_bus.c sdio_io.c sdio.c sd.c
<core.c>
static int __init mmc_init(void){... workqueue = alloc_ordered_workqueue("kmmcd", 0);... ret = mmc_register_bus();... ret = mmc_register_host_class();... ret = sdio_register_bus();...}
这个函数一开始建立了一个工作队列workqueue,这个工作队列的作用主要是用来支持热插拔;然后分别注册一个mmc总线mmc_bus_type,一个mmc_host类,一个 sdio总线 sdio_bus_type。
3) < kernel/driver/mmc/host>
host目录下的驱动文件是mmc或者sd卡的通讯接口驱动
scxxxx.c sdhci.c
<scxxxx.c>
static int __init sdhci_sprd_init(void){ return platform_driver_register(&sdhci_sprd_driver);}
该函数只是注册了一个 platform 总线上的驱动,由于我们的 mmc 是通过 platform 总线上的设备 mmc_host 接口来通讯的,所以这个驱动实则上就是最底层的硬件接口驱动。当然,在这之前,我们得注册一个 platform 总线,并在该 platform 总线上挂载了一个 mmc_host 接口设备。这样,在注册 platform 驱动的时候,就会调用该驱动中的 probe 函数。Probe函数中调用了函数:
host = sdhci_alloc_host(dev, sizeof(struct sprd_host_data));//申请 private 结构体 sprd_host_data host_data = sdhci_priv(host);//赋值为 host->privatesdhci_host结构体的最后一个成员为:unsigned long private[0] , private 指向sdhci_host结构体之后的地址,private本身并不分配空间。所以,通过host->private即可访问host_data.此为内核中的常用机制。 进一步调用:
mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);创建一个mmc_host结构,这个结构就是在mmc_card所需要的mmc主机实例。
host = mmc_priv(mmc);host->mmc = mmc;在mmc_alloc_host中,创建一个mmc_host和 sdhci_host 和 sprd_host_data,且mmc_host的最后一个成员指针private指向 sdhci_host; sdhci_host 的 mmc指针指向mmc_host。 除此之外,mmc_alloc_host()最重要的作用是建立了一个工作队列任务
INIT_DELAYED_WORK(&host->detect, mmc_rescan);工作队列任务执行的函数为mmc_rescan,在这个函数中以不同的频率扫描:
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq){... mmc_power_up(host);... mmc_hw_reset_for_init(host);... sdio_reset(host); mmc_go_idle(host); mmc_send_if_cond(host, host->ocr_avail);... if (!mmc_attach_sdio(host)) return 0; if (!host->ios.vdd) mmc_power_up(host); if (!mmc_attach_sd(host)) return 0; if (!host->ios.vdd) mmc_power_up(host); if (!mmc_attach_mmc(host)) return 0; mmc_power_off(host);...}从这个函数看到,一开始就是设置某一个时钟频率,然后对mmc或者sd发送一些命令进行探测。 接着看mmc_attach_mmc()
int mmc_attach_mmc(struct mmc_host *host){...err = mmc_init_card(host, host->ocr, NULL);...err = mmc_add_card(host->card);...}一开始设置mmc的电压,然后就对mmc卡进行初始化,主要是读取mmc卡里的一些寄存器信息,且对这些寄存器的值进行设置。然后,调用mmc_add_card来把mmc_card挂载到mmc_bus_type总线上去:
int mmc_add_card(struct mmc_card *card){...ret = device_add(&card->dev);...}接着调用 device_add()
int device_add(struct device *dev){...bus_probe_device(dev);...}然后调用bus_probe_device()
void bus_probe_device(struct device *dev){...ret = device_attach(dev);...}这样,在总线mmc_bus_type中就有了mmc设备mmc_card了。 probe 函数的一个重要初始化操作:
host->ops = &sdhci_sprd_ops;这个 sdhci_sprd_ops 操作函数就是 sdhci 接口的操作函数。 不过这里有个问题,就是这个工作队列任务什么时候开始执行呢,又与 (1) 部分创建的工作队列workqueue有什么关系,跟热插拔有什么关系。
继续把焦点放到probe函数:
{...host_data->platdata = dev_get_platdata(dev);...sd_detect_gpio = host_data->platdata->detect_gpio;...detect_irq = gpio_to_irq(sd_detect_gpio);...host_data->detect_irq = detect_irq;...ret = sdhci_add_host(host);...}
这里有个 detect_irq 的变量,保存了设备中断引脚号。当sd卡插入时检测到一个外部中断。
int sdhci_add_host(struct sdhci_host *host){...host_data = sdhci_priv(host);detect_irq = host_data->detect_irq;if (sdcard_present(host)) { ret = request_threaded_irq(detect_irq, NULL, sd_detect_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "sd card detect", host); } else { ret = request_threaded_irq(detect_irq, NULL, sd_detect_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "sd card detect", host); }...sdhci_init(host, 0);...mmc_add_host(mmc);...sdhci_enable_card_detection(host);...}
打开SD卡检测
static void sdhci_enable_card_detection(struct sdhci_host *host){ sdhci_set_card_detection(host, true);}进入sdhci_set_card_detection()
static void sdhci_set_card_detection(struct sdhci_host *host, bool enable){ int irq; struct sprd_host_data *host_data; host_data = sdhci_priv(host); irq = host_data->detect_irq; if(irq > 0){ if(!enable) { irq_set_irq_type(irq,IRQF_TRIGGER_NONE); return; } if(sdcard_present(host)){ irq_set_irq_type(irq,IRQF_TRIGGER_HIGH); }else{ irq_set_irq_type(irq,IRQF_TRIGGER_LOW); } }}<block.c> module_init(mmc_blk_init); <mmc_test.c> module_init(mmc_test_init); <scxxxx.c> module_init(sdhci_sprd_init); <sdhci.c> module_init(sdhci_drv_init);
转载地址:http://fhbqi.baihongyu.com/