其中,remove为probe的反操作,suspend和resume涉及电源管理的内容,本文重点讨论probe。
SD主控制器驱动程序的初始化函数probe(struct platform_device *pdev),概括地讲,主要完成五大任务,
? ? ? ? ?
初始化设备的数据结构,并将数据挂载到pdev->dev.driver_data下 实现设备驱动的功能函数,如mmc->ops = &pxamci_ops; 申请中断函数request_irq()
注册设备,即注册kobject,建立sys文件,发送uevent等 其他需求,如在/proc/driver下建立用户交互文件等
2.2.1 注册设备
对于设备的注册,所有设备驱动的相关代码都类似。 static int pxamci_probe(struct platform_device *pdev) {
mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev); mmc_add_host(mmc); ... ... }
这两个函数都由/drivers/mmc/core核心层下的host.c负责具体实现, 1) mmc_alloc_host
为主设备控制器建立数据结构,建立kobject,并初始化等待队列,工作队列,以及一些控制器的配置。其中,INIT_DELAYED_WORK(&host->detect, mmc_rescan);将探测SD卡的函数mmc_rescan与工作队列host->detect关联,mmc_rescan是整个SD子系统的核心函数,本文第三部分协议层将对它作重点讨论。
struct mmc_host *mmc_alloc_host(int extra, struct device *dev) {
/* 建立数据结构 */ struct mmc_host *host;
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); /* 建立kobject */ host->parent = dev;
host->class_dev.parent = dev;
host->class_dev.class = &mmc_host_class; device_initialize(&host->class_dev); /* 初始化等待队列,工作队列 */ init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
/* 配置控制器 */
host->max_hw_segs = 1; host->max_phys_segs = 1; ... ... return host; }
2) mmc_add_host
完成kobject的注册,并调用mmc_rescan,目的在于在系统初始化的时候就扫描SD总线查看是否存在SD卡。注意到这里的工作队列的延时时间delay为0,因为系统启动的时候不考虑插拔SD卡,关于这个delay将在下文讨论。 int mmc_add_host(struct mmc_host *host) {
device_add(&host->class_dev); mmc_start_host(host);
... ... }
void mmc_start_host(struct mmc_host *host) {
mmc_power_off(host); mmc_detect_change(host, 0); }
void mmc_detect_change(struct mmc_host *host, unsigned long delay) {
mmc_schedule_delayed_work(&host->detect, delay); }
static int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay) {
wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ * 2); return queue_delayed_work(workqueue, work, delay); }
2.2.2 为设备赋初值
其实,整个设备驱动的probe()函数,其本质就是是为设备建立起数据结构并对其赋初值。pxamci_probe(struct platform_device *pdev)主要为SD主控制器完成时钟、存储等方面的初始化配置,
static int pxamci_probe(struct platform_device *pdev) {
struct mmc_host *mmc;
struct pxamci_host *host = NULL;
mmc->ops = &pxamci_ops; mmc->max_phys_segs = NR_SG; mmc->max_hw_segs = NR_SG; mmc->max_seg_size = PAGE_SIZE; host = mmc_priv(mmc); host->mmc = mmc; host->dma = -1;
host->pdata = pdev->dev.platform_data; host->clkrt = CLKRT_OFF;
host->clk = clk_get(&pdev->dev, \ host->clkrate = clk_get_rate(host->clk);
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW, pxamci_dma_irq, host); ... ... }
完成所有赋值后,通过platform_set_drvdata(pdev, mmc);将数据挂载到pdev->dev.driver_data。
所有赋值中,我们重点关注从platform_device *pdev里得到的数据。platform_device *pdev是在系统初始化的时候扫描platform总线发现SD主控制器后所得到的数据。 1) 得到platform_data数据 先看看platform_device的结构, struct platform_device { const char * name; int id;
struct device dev; u32 num_resources; struct resource * resource; };
系统初始化的时候,已经为该SD主控制器的name, resources等赋上了初值,具体内容如下,
struct platform_device pxa_device_mci = { .name = \pxa2xx-mci\ .id = 0, .dev = {
.dma_mask = &pxamci_dmamask, .coherent_dma_mask = 0xffffffff, },
.num_resources = ARRAY_SIZE(pxamci_resources),
.resource = pxamci_resources, };
static struct resource pxamci_resources[] = { [0] = {
.start = 0x41100000, .end = 0x41100fff,
.flags = IORESOURCE_MEM, // SD主控制器芯片的起始地址 }, [1] = {
.start = IRQ_MMC, /* #define IRQ_MMC 23 */ .end = IRQ_MMC,
.flags = IORESOURCE_IRQ, // 申请的中断号 }, ... ... };
需要注意的是,platform_device数据结构里的name, id, resource等是所有设备都用的到的数据类型,那么设备自身独有的特性如何表现出来呢?事实上,结构体device专门准备了一个成员platform_data,就是为了挂载设备的一些特有的数据。(注意与driver_data相区别) struct device {
void *platform_data; /* Platform specific data, device core doesn't touch it */ void *driver_data; /* data private to the driver */ ... ... }
看看SD主控制器为什么会有这些特有数据,
static struct pxamci_platform_data saar_mci_platform_data = { .detect_delay = 50,
.ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, .init = saar_mci_init, .exit = saar_mci_exit, };
-> detect_delay
就是刚才提到的工作队列的延时时间,设置为50ms,由于各种SD主控制器芯片的性能不同,这个值可能会变化。那么为什么要为工作队列加一个延迟呢?首先,当插入SD卡之后,SD主控制器上的探测引脚会产生一个中断,之后调用中断函数里的工作队列,然后执