linux sdio总线驱动下载,Linux 驱动分析--s3c6410 的SDIO驱动分析

转载请注明作者及网址.

正在研究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的模块分析,参见我的博文

首先根据档和源码来看三星发了几个关健的BSP,一个是针对linux 2.6.21.另外一个针对 linux 2.6.28的。这两个对SDIO的驱动采用不同的实际,有一篇官方文档可以看看,里面对Linux 2.6.21 SDIO实现分析得详细。

<>,但是网上只能找到这一个

如果你对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 = {

.name  = "sdio",

.dev_attrs = sdio_dev_attrs,

.match  = sdio_bus_match,

.uevent  = sdio_bus_uevent,

.probe  = sdio_bus_probe,

.remove  = sdio_bus_remove,

};

int sdio_register_bus(void)

{

return bus_register(&sdio_bus_type);

}

void sdio_unregister_bus(void)

{

bus_unregister(&sdio_bus_type);

}

而对应总线驱动struct device_driver 是 struct sdio_driver

/*

* SDIO function device driver

*/

struct sdio_driver {

char *name;

const struct sdio_device_id *id_table;

int (*probe)(struct sdio_func *, const struct sdio_device_id *);

void (*remove)(struct sdio_func *);

struct device_driver drv;};

它使用 如下两个函数来注册和销毁sdio_driver.

extern int sdio_register_driver(struct sdio_driver *);

extern void sdio_unregister_driver(struct sdio_driver *);

而对应总线设备的就是 struct device  是 struct sdio_func,这个名字与device差别太大了,因此我一开没有太在意。

/*

* SDIO function devices

*/

struct sdio_func {

struct mmc_card  *card;  /* the card this device belongs to */

struct device  dev;  /* the device */ sdio_irq_handler_t *irq_handler; /* IRQ callback */

unsigned int  num;  /* function number */

unsigned char  class;  /* standard interface class */

unsigned short  vendor;  /* vendor id */

unsigned short  device;  /* device id */

unsigned  max_blksize; /* maximum block size */

unsigned  cur_blksize; /* current block size */

unsigned  enable_timeout; /* max enable timeout in msec */

unsigned int  state;  /* function state */

#define SDIO_STATE_PRESENT (1<<0)  /* present in sysfs */

u8   tmpbuf[4]; /* DMA:able scratch buffer */

unsigned  num_info; /* number of info strings */

const char  **info;  /* info strings */

struct sdio_func_tuple *tuples;

};

对于sdio_func操作

int sdio_add_func(struct sdio_func *func);

void sdio_remove_func(struct sdio_func *func);

在drivers/net/wireless/libertas/if_sdio.c

它定义了如下的 sdio_driver

static struct sdio_driver if_sdio_driver = {

.name  = "libertas_sdio",

.id_table = if_sdio_ids,

.probe  = if_sdio_probe,

.remove  = if_sdio_remove,

};

static int __init if_sdio_init_module(void)

{

int ret = 0;

ret = sdio_register_driver(&if_sdio_driver);

return ret;

}

static void __exit if_sdio_exit_module(void)

{

sdio_unregister_driver(&if_sdio_driver);

}

SDIO采用的与MMC兼容的命令接口,因此对于mmc的操作,LINUX又封装一层,使用数据结构 mmc_host

struct mmc_host {

struct device  *parent;

struct device  class_dev; int   index;

const struct mmc_host_ops *ops; unsigned int  f_min;

unsigned int  f_max;

u32   ocr_avail;

unsigned long  caps;  /* Host capabilities */

/* host specific block data */

unsigned int  max_seg_size; /* see blk_queue_max_segment_size */

unsigned short  max_hw_segs; /* see blk_queue_max_hw_segments */

unsigned short  max_phys_segs; /* see blk_queue_max_phys_segments */

unsigned short  unused;

unsigned int  max_req_size; /* maximum number of bytes in one req */

unsigned int  max_blk_size; /* maximum size of one mmc block */

unsigned int  max_blk_count; /* maximum number of blocks in one req */

/* private data */

spinlock_t  lock;  /* lock for claim and bus ops */

struct mmc_ios  ios;  /* current io bus settings */

u32   ocr;  /* the current OCR setting */

/* group bitfields together to minimize padding */

unsigned int  use_spi_crc:1;

unsigned int  claimed:1; /* host exclusively claimed */

unsigned int  bus_dead:1; /* bus has been released */

#ifdef CONFIG_MMC_DEBUG

unsigned int  removed:1; /* host is being removed */

#endif

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  bus_refs; /* reference counter */

unsigned int  sdio_irqs;

struct task_struct *sdio_irq_thread;

atomic_t  sdio_irq_thread_abort;

#ifdef CONFIG_LEDS_TRIGGERS

struct led_trigger *led;  /* activity led */

#endif

struct dentry  *debugfs_root;

unsigned long  private[0] ____cacheline_aligned;

};

而mmc_host是一个特殊内核类 mmc_host,它会在 /sys/class/ 建立一个mmc_host类.

//drivers/mmc/core/host.c

static struct class mmc_host_class = {

.name  = "mmc_host",

.dev_release = mmc_host_classdev_release,

};

int mmc_register_host_class(void)

{

return class_register(&mmc_host_class);

}

void mmc_unregister_host_class(void)

{

class_unregister(&mmc_host_class);

}

mmc_host用于描述MMC接口 CPU一侧的设备,它可以看成是class device的特例,而插入的SDIO设备,如SD卡则封装成数据结构,如果mmc_host发送命令和数据是通过 struct mmc_host_ops 操作

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);

};

如果SDIO设备操作,直接采用如下定义

static struct mmc_host_ops s3c_hsmmc_ops = {

.request = s3c_hsmmc_request,

.set_ios = s3c_hsmmc_set_ios,

};

mmc_card /mmc_bus

如果SD卡的之存储设备进行块设备操作,则需要另一个额外的mmc bus总线,并且需要定义mmc_card结构,对应(device).并且进行存储的块设备操作定义在mmc_driver类。

如果SDIO设备本身不需要这个总线

在mmc又单独定义一个 mmc总线,

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,

};

int mmc_register_bus(void)

{

return bus_register(&mmc_bus_type);

}

void mmc_unregister_bus(void)

{

bus_unregister(&mmc_bus_type);

}

mmc_card.它可以看一个bus device 设备

// include/linux/mmc/card.h

/*

* MMC device

*/

struct mmc_card {

struct mmc_host  *host;  /* the host this device belongs to */

struct device  dev;  /* the device */

unsigned int  rca;  /* relative card address of device */

unsigned int  type;  /* card type */

#define MMC_TYPE_MMC  0  /* MMC card */

#define MMC_TYPE_SD  1  /* SD card */

#define MMC_TYPE_SDIO  2  /* SDIO card */

unsigned int  state;  /* (our) card state */

#define MMC_STATE_PRESENT (1<<0)  /* present in sysfs */

#define MMC_STATE_READONLY (1<<1)  /* card is read-only */

#define MMC_STATE_HIGHSPEED (1<<2)  /* card is in high speed mode */

#define MMC_STATE_BLOCKADDR (1<<3)  /* card uses block-addressing */

u32   raw_cid[4]; /* raw card CID */

u32   raw_csd[4]; /* raw card CSD */

u32   raw_scr[2]; /* raw card SCR */

struct mmc_cid  cid;  /* card identification */

struct mmc_csd  csd;  /* card specific */

struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */

struct sd_scr  scr;  /* extra SD information */

struct sd_switch_caps sw_caps; /* switch (CMD6) caps */

unsigned int  sdio_funcs; /* number of SDIO functions */

struct sdio_cccr cccr;  /* common card info */

struct sdio_cis  cis;  /* common tuple info */

struct sdio_func *sdio_func[SDIO_MAX_FUNCS]; /* SDIO functions (devices) */

unsigned  num_info; /* number of info strings */

const char  **info;  /* info strings */

struct sdio_func_tuple *tuples; /* unknown common tuples */

struct dentry  *debugfs_root;

};

mmc_card 与 sdio 总线的device (sdio_func)的交互的关系体现在 mmc_card中sdio_func定义

而mmc_card与mmc_host关系体现在定义 mmc_card的host定义中

而mmc_bus中,其驱动就是mmc_driver

/*

* MMC device driver (e.g., Flash card, I/O card...)

*/

struct mmc_driver {

struct device_driver drv;

int (*probe)(struct mmc_card *);

void (*remove)(struct mmc_card *);

int (*suspend)(struct mmc_card *, pm_message_t);

int (*resume)(struct mmc_card *);

};

extern int mmc_register_driver(struct mmc_driver *);

extern void mmc_unregister_driver(struct mmc_driver *)

而使用了mmc_driver 好象只有处理SD卡块设备

//driver/mmc/core/block.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,

};

它大体有如下关系,这里出问题就是BSP这一侧 sdio channel1的驱动

110505235056.png

Linux 2.6.21的SDIO的实现

在Linu 2.6.21的SDIO驱动,它是除了是mmc_host的驱动外,还是一个Platform驱动.身兼两职.

110506000927.png

在这个版本中,首先是定义三个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,

}

};

其platform_driver 定义在drivers/mmc/s3c-hsmmc.c

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);

}

在s3c_hsmmc_probe函数中,会进行一系列的初始化操作.

110506003117.png

Linux 2.6.28的SDIO的实现

Linux 2.6.28又做了进一步调整,原来的s3c-hsmmc的设备和驱动已经放弃,原来在s3c-hsmmc.c 已经成为残余代码,虽然在内核中,但是不会加入Makefile编译。

其新的platform_device 定义分布定义在 dev-hsmmc.c /dev-hsmmc1.c / dev-hsmmc2.c,可以看到设备名字已经变成了 s3c-sdhci

struct s3c_sdhci_platdata s3c_hsmmc0_def_platdata = {

.max_width = 4,

.host_caps = (MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED |

MMC_CAP_SD_HIGHSPEED),

.cfg_ext_cd = setup_sdhci0_irq_cd,

.detect_ext_cd = detect_sdhci0_irq_cd,

.ext_cd  = S3C_EINT(13),

};

struct platform_device s3c_device_hsmmc0 = {

.name  = "s3c-sdhci", .id  = 0,

.num_resources = ARRAY_SIZE(s3c_hsmmc_resource),

.resource = s3c_hsmmc_resource,

.dev  = {

.dma_mask  = &s3c_device_hsmmc_dmamask,

.coherent_dma_mask = 0xffffffffUL,

.platform_data  = &s3c_hsmmc0_def_platdata,

},

};

它会在系统初始化注册deivce.

而platform_driver定义在drivers/mmc/host/sdhci-s3c.c

//drivers/mmc/host/sdhci-s3c.c

static struct platform_driver sdhci_s3c_driver = {

.probe  = sdhci_s3c_probe,

.remove  = __devexit_p(sdhci_s3c_remove),

.suspend        = sdhci_s3c_suspend,

.resume         = sdhci_s3c_resume,

.driver  = {

.owner = THIS_MODULE,

.name = "s3c-sdhci", },

};

在测试WIFI模块时,在 /sys/bus/platform/devices中已经看到了 这两个device,但是在/sys/bus/sdio/deivces仍然没见到相应设备

[root@urbetter bin]# ls /sys/bus/platform/devices

dm9000_con201.0s3c-keypads3c-tvscalers3c6400-uart.1

onenands3c-lcds3c-usbgadgets3c6400-uart.2

regulatory.0s3c-mfcs3c-vpps3c6400-uart.3

s3c-fimc.0s3c-rotators3c2410-ohcis3c6410-nand

s3c-fimc.1s3c-sdhci.0s3c2410-rtcs3c_otghcd

s3c-g2ds3c-sdhci.1s3c2410-wdtsam-spi.0

s3c-g3ds3c-tss3c2440-i2csam-spi.1

s3c-jpegs3c-tvencs3c6400-uart.0soc-audio

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值