SDIO驱动总结

By Dybinx

内核:Linux 2.6.35.7

硬件:三星的s5pv210平台

SDIO驱动包括SDIO 控制器驱动和SDIO卡驱动,属于主从结构类型,相信看过usb驱动的朋友会更容易理解SDIO的驱动框架。

本文初略分析了SDIO控制器驱动与SDIO卡驱动的调用过程,待以再对SDIO做进一步分析。

1. SDIO 控制器驱动

三星的s5pv210平台使用的SDIO控制器涉及到的驱动文件主要.\ drivers \mmc\host目录下的sdhci.c sdhci-s3c.c文件,主要的数据结构如下:

Ø  struct sdhci_s3c

Ø  struct sdhci_host

Ø  struct mmc_host

它们之间的关系如下图:

struct sdhci_host结构体包含了struct mmc_host结构体指针,而struct mmc_host机构体里包含了struct mmc_card结构体,正是这种层层嵌套的关系才最终构建了SDIO子系统结构框架。

SDIO控制器作为平台设备(platform_device),在系统初始化的时候注册在平台总线中。另外,当对于的平台驱动加载时会调用驱动注册方法,这个过程是从sdhci-s3c.c文件的模块初始化函数开始的。

驱动注册后,在平台总线找哦你查找SDIO控制器设备,如果找到,就会调用sdhci_s3c_probe(struct platform_device*pdev)函数,这个函数完成上面提到的三个结构体的初始化和注册工作,最终添加到设备模型中。

sdhci_s3c_probe(struct platform_device*pdev)调用过程:

首先通过传入的平台设备获得平台资源,然后调用

sdhci_alloc_host(dev, sizeof(struct sdhci_s3c)),后者又调用mmc_alloc_host(sizeof(struct sdhci_host) +priv_size, dev)函数,最终调用kzalloc(sizeof(struct mmc_host) + extra,GFP_KERNEL)分配内存空间,从传入的参数可以知道,sdhci_alloc_host(dev, sizeof(structsdhci_s3c))函数返回后,系统为struct sdhci_s3c 、struct sdhci_host、struct mmc_host分配了连续的存储空间,而且有结构体里的struct mmc_host结构体的private数组存储struct sdhci_host,后者的private存储struct sdhci_s3c,其内存结构图如下:

sdhci_s3c_probe(struct platform_device*pdev)函数里另外一个重量级的函数是sdhci_add_host(host)函数,这个函数一个重要的工作就是调用request_irq(host->irq, sdhci_irq,IRQF_SHARED,mmc_hostname(mmc), host),为SDIO控制器注册中断,中断处理函数为sdhci_irq(int irq, void *dev_id),它完成传递到SDIO控制器的中断处理,其中包括SDIO卡设备插入或拔出时的信号处理。

当SDIO卡设备插入系统时,硬件产生中断信号,最终调用sdhci_irq(int irq, void *dev_id),这个函数调用tasklet_schedule(&host->card_tasklet)函数,其中host->card_tasklet在mmc_alloc_host(int extra, struct device*dev)函数里调用INIT_DELAYED_WORK(&host->detect, mmc_rescan)函数初始化为mmc_rescan()函数,即最终调由mmc_rescan()函数来处理SDIO卡设备的插入操作。这个函数针对SDIO、SD、MMC卡设备分别调用不同的函数处理。对于SDIO卡设备,它调用的是mmc_attach_sdio(host, ocr)函数,这个函数调用mmc_sdio_init_card(host, host->ocr,NULL, 0)将初始化一个SDIO设备,然后将其注册到MMC总线上,调用sdio_init_func(host->card, i + 1)函数初始化SDIO设备,并将其注册到SDIO总线上。需要注意到是注册到MMC总线上的是struct mmc_card结构体,注册到SDIO总线上的是struct sdio_func结构体,而sdio_func里包含mmc_card结构体指针,mmc_card结构体包含struct sdio_func结构体指针数组,可见struct sdio_func结构体是struct mmc_card结构体的一个子项,或者说sdio_func表示的是SDIO设备,而这种设备具有mmc_card设备的功能,相对于mmc_card设备,sdio_func是更加具体的一种设备。

还要说明的一点是,设备是挂接在总线上的,每一个控制器至少对于拥有一条总线,那么,MMC子系统的总线是在什么时候产生的呢?在mmc\core子目录下有一个core.c文件,在文件的底端有一段代码:subsys_initcall(mmc_init),了解系统启动流程的朋友一定对subsys_initcall不陌生,它是系统初始化代码段的一个索引,它的参数所对应的代码会在系统启动的时候被调用,在mmc_init函数里建立了mmc子系统要用到的总线。

SDIO卡驱动

根据不同的设备也对应不同的SDIO卡驱动,本文主要分析Marvell 8688 SDIO WIFI的驱动注册过程。

这个文件在.\drivers\net\wireless\libertas目录下。

既然是总线设备,SDIO驱动无疑也要注册到设备模型中,并且在找到sdio设备后调用它的probe函数,然后进入漫长的设备初始化过程中。怎么又是设备初始化,从if_sdio_probe函数里不是传入了一个设备结构体(sdio_func)吗?为什么还要初始化设备结构体?正如自然界逃不开物种多样性一样,Linux内核也需要设备多样性。sdio_func仅仅代表的是一个通用的sdio设备,当sdio设备插入系统时,sdio控制器只知道需要生成一个sdio_func结构体来表示sdio设备,至于这个设备还有什么特殊功能,它就管不了那么多了,这就需要具体的Probe函数来处理,为这个设备添加特殊的功能,具体到wifi设备,这个结构体就是if_sdio_card,它与sdio_func之间的关系可以从if_sdio_card设备结构体里看到,里面包含一个sdio_func结构体指针。总而言之,这个函数就是针对wifi设备建立一个具有wifi设备特色的结构体出来,直到if_sdio_probe结束,这时候这个sdio wifi才算刚刚进入正轨,开始工作了。

本文仅仅是初略的介绍了sdio控制器建立以及sdio设备从无到有的过程,具体到sdio控制器的细节,还需要大量的工作,包括熟悉相应的总线协议。而对于sdio wifi驱动也仅仅是开了一个头而已,对于它的细节,将又是一个不同的领域。人生就像一场旅行,在乎的不是目的地,而是沿途的风景,而只有细细琢磨才能体会每一处风景的独特之处,Linux内核又何尝不是如此呢。感慨归感慨,上述分析有不当之处,请各位指点。