正在研究mw8686 的Linux SDIO驱动,编译好后在s3c6410运行,发现未能出现网络接口设备。一查之下,它使用sdio总线进行操作驱动和设备,但是在 /sys/bus/sdio/devices 没有看到任何设备。因此推理是这个导致WiFi无法使用SDIO的原因。因为sdio驱动的初始化是放在probe当中,而probe是在sdio设备与sdio driver的match的过程中执行中。没有设备,意味着无法进行SDIO初始化。
我用的是Linux 2.6.28的版本,在网络上找到几个其它版本,主要Linux 2.6.21的,利用下午上课时间,进行一下课堂实验。发现这两个版本的实现差别很大,实际上两个分水岭。刚开始没明白这个道理,被两个版本的的源码搞得很混乱,因此为了解决问题,对这个两版本的SDIO实现进行分析
关于mw8686的模块分析,参见我的博文 < span style='font-size:12px;font-style:normal;font-weight:normal;font-family:song, Verdana;color:rgb(102, 102, 102);' >S3C6410硬件模块分析 -- SDIO WiFi模块分析>></span> http://blogold.chinaunix.net/u3/105675/showart_2536780.html
首先根据档和源码来看三星发了几个关健的BSP,一个是针对linux 2.6.21.另外一个针对 linux 2.6.28的。这两个对SDIO的驱动采用不同的实际,有一篇官方文档可以看看,里面对Linux 2.6.21 SDIO实现分析得详细。 < span style='font-size:12px;font-style:normal;font-weight:normal;color:rgb(51, 51, 51);' >SMDK6400 6410 HS MMC porting Guide v1.0</span> <<,但是网上只能找到这一个 http://wenku.baidu.com/view/1560b017866fb84ae45c8d10.html 如果你对Linux 2.6的驱动模型相当了解,实际也能很快分析清楚。
SDIO总线
首先mw8686的if_sdio.c直接使用了sdio 总线。这个虚拟总线是由 driver/mmc/core/sdio_bus.c实现,在两个版本,这个基本是变化不大的地方。要注意,这里的SDIO和MMC是两个兼容的接口,因此在源码中两个术语经常互换. sdio bus是一个标准的Linux 的总线,因此它需要有标准 bus device 和标准bus driver来注册到系统中。 这是 sdio_bus.c的总线定义
static struct bus_type sdio_bus_type = { int sdio_register_bus(void) void sdio_unregister_bus(void) |
而对应总线驱动struct device_driver 是 struct sdio_driver
/* int (*probe)(struct sdio_func *, const struct sdio_device_id *); struct device_driver drv; |
extern void sdio_unregister_driver(struct sdio_driver *); 而对应总线设备的就是 struct device 是 struct sdio_func,这个名字与device差别太大了,因此我一开没有太在意。
/* unsigned char class; /* standard interface class */ unsigned max_blksize; /* maximum block size */ unsigned enable_timeout; /* max enable timeout in msec */ unsigned int state; /* function state */ u8 tmpbuf[4]; /* DMA:able scratch buffer */ unsigned num_info; /* number of info strings */ struct sdio_func_tuple *tuples; |
void sdio_remove_func(struct sdio_func *func); 在drivers/net/wireless/libertas/if_sdio.c 它定义了如下的 sdio_driver
static struct sdio_driver if_sdio_driver = { static int __init if_sdio_init_module(void) |
struct mmc_host { /* private data */ struct mmc_ios ios; /* current io bus settings */ /* group bitfields together to minimize padding */ struct mmc_card *card; /* device attached to this host */ wait_queue_head_t wq; struct delayed_work detect; const struct mmc_bus_ops *bus_ops; /* current bus driver */ unsigned int sdio_irqs; #ifdef CONFIG_LEDS_TRIGGERS struct dentry *debugfs_root; unsigned long private[0] ____cacheline_aligned; |
//drivers/mmc/core/host.c static struct class mmc_host_class = { int mmc_register_host_class(void) void mmc_unregister_host_class(void) |
struct mmc_host_ops { void (*request)(struct mmc_host *host, struct mmc_request *req); void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios); int (*get_ro)(struct mmc_host *host); int (*get_cd)(struct mmc_host *host); void (*enable_sdio_irq)(struct mmc_host *host, int enable); }; |
static struct mmc_host_ops s3c_hsmmc_ops = { .request = s3c_hsmmc_request, .set_ios = s3c_hsmmc_set_ios, }; |
static struct bus_type mmc_bus_type = { int mmc_register_bus(void) void mmc_unregister_bus(void) |
// include/linux/mmc/card.h /* u32 raw_cid[4]; /* raw card CID */ unsigned int sdio_funcs; /* number of SDIO functions */ struct dentry *debugfs_root; |
/* extern int mmc_register_driver(struct mmc_driver *); |
//driver/mmc/core/block.c static struct mmc_driver mmc_driver = { |
它大体有如下关系,这里出问题就是BSP这一侧 sdio channel1的驱动
Linux 2.6.21的SDIO的实现 在Linu 2.6.21的SDIO驱动,它是除了是mmc_host的驱动外,还是一个Platform驱动.身兼两职. 在这个版本中,首先是定义三个platform_deivce 以注册到系统当中。但是它定义在 arch/arm/plat-s3c24xx/devs.c (这会让人感当相当迷惑),它创建是 s3c-hsmmc设备
struct platform_device s3c_device_hsmmc0 = { .name = "s3c-hsmmc", .id = 0, .num_resources = ARRAY_SIZE(s3c_hsmmc0_resource), .resource = s3c_hsmmc0_resource, .dev = { .platform_data = &s3c_hsmmc0_platform, } }; struct platform_device s3c_device_hsmmc1 = { .name = "s3c-hsmmc", .id = 1, .num_resources = ARRAY_SIZE(s3c_hsmmc1_resource), .resource = s3c_hsmmc1_resource, .dev = { .platform_data = &s3c_hsmmc1_platform, } }; |
static struct platform_driver s3c_hsmmc_driver = { .probe = s3c_hsmmc_probe, .remove = s3c_hsmmc_remove, .suspend = s3c_hsmmc_suspend, .resume = s3c_hsmmc_resume, .driver = { .name = "s3c-hsmmc", .owner = THIS_MODULE, }, }; static int __init s3c_hsmmc_drv_init(void) { return platform_driver_register(&s3c_hsmmc_driver); } static void __exit s3c_hsmmc_drv_exit(void) { platform_driver_unregister(&s3c_hsmmc_driver); } |
struct s3c_sdhci_platdata s3c_hsmmc0_def_platdata = { struct platform_device s3c_device_hsmmc0 = { |
//drivers/mmc/host/sdhci-s3c.c static struct platform_driver sdhci_s3c_driver = { |
[root@urbetter bin]# ls /sys/bus/platform/devicesdm9000_con201.0s3c-keypads3c-tvscalers3c6400-uart.1onenands3c-lcds3c-usbgadgets3c6400-uart.2regulatory.0s3c-mfcs3c-vpps3c6400-uart.3s3c-fimc.0s3c-rotators3c2410-ohcis3c6410-nands3c-fimc.1s3c-sdhci.0s3c2410-rtcs3c_otghcds3c-g2ds3c-sdhci.1s3c2410-wdtsam-spi.0s3c-g3ds3c-tss3c2440-i2csam-spi.1s3c-jpegs3c-tvencs3c6400-uart.0soc-audio |