sdio 总线驱动分析

今天看了下mmc/sd/sdio模块的代码,做下笔记,便于以后翻看。

有3个文件夹:
linux-2.6/drivers/mmc$ ls
card  core  host  Kconfig  Makefile

card--是支持sdcard用的
core--是支持sd总线协议的
host--是各种CPU的sd-host模块

sd模块开始是在/core/core.c 中的 mmc_init(), 如下:

static int __init mmc_init(void)
2204 {
2205 int ret;
2206
2207 wake_lock_init(&mmc_delayed_work_wake_lock, WAKE_LOCK_SUSPEND, "mmc_delayed_work");
2208
2209 workqueue = create_singlethread_workqueue("kmmcd");//创建单线程的工作队列
2210 if (!workqueue)
2211 return -ENOMEM;
2212
2213 ret = mmc_register_bus();  //调用到 bus.c 中去
2214 if (ret)
2215 goto destroy_workqueue;
2216
2217 ret = mmc_register_host_class(); //调用到 host.c 中去
2218 if (ret)
2219 goto unregister_bus;
2220
2221 ret = sdio_register_bus(); //调用到 sdio_bus.c 中去
2222 if (ret)
2223 goto unregister_host_class;
2224
2225 return 0;
2226
2227 unregister_host_class:
2228 mmc_unregister_host_class();
2229 unregister_bus:
2230 mmc_unregister_bus();
2231 destroy_workqueue:
2232 destroy_workqueue(workqueue);
2233
2234 return ret;
2235 }
2236
2237 static void __exit mmc_exit(void)
2238 {
2239 sdio_unregister_bus();
2240 mmc_unregister_host_class();
2241 mmc_unregister_bus();
2242 destroy_workqueue(workqueue);
2243 wake_lock_destroy(&mmc_delayed_work_wake_lock);
2244 }
2245
2246 subsys_initcall(mmc_init);
2247 module_exit(mmc_exit);
2248
2249 MODULE_LICENSE("GPL");
mmc_register_bus();  //注册总线,调用到 bus.c 中去

int mmc_register_bus(void)
{
    return bus_register(&mmc_bus_type);
}
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,
    .pm        = MMC_PM_OPS_PTR,
};
这个数组mmc_bus_type里,包含了很多函数了,都是处理总线mmc_bus的.这里,我们主要关注mmc_bus_match与mmc_bus_probe,此两函数将要在device_register和driver_register向总线注册设备的时候被调用。
/*
 * This currently matches any MMC driver to any MMC card - drivers
 * themselves make the decision whether to drive this card in their
 * probe method.
 */
static int mmc_bus_match(struct device *dev, struct device_driver *drv)
{
    return 1;  //目前匹配所有的MMC卡。
}
static int mmc_bus_probe(struct device *dev)
{
    struct mmc_driver *drv = to_mmc_driver(dev->driver);
    struct mmc_card *card = mmc_dev_to_card(dev);

    return drv->probe(card);
}
在使用bus_register之后,我们可以在sysfs的/sys/bus目录里看到它。

总线注册完成后,接着是(); //调用到 host.c 中去

int mmc_register_host_class(void)
{
    return class_register(&mmc_host_class);
}
mmc_register_host_classclass_register之后,在 /sys /class目录下将出现mmc_host目录。
static struct class mmc_host_class = {
    .name        = "mmc_host",
    .dev_release    = mmc_host_classdev_release,
};
static void mmc_host_classdev_release(struct device *dev)
{
    struct mmc_host *host = cls_dev_to_mmc_host(dev);
    kfree(host);
}


#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
完成host_class 注册后,就轮到 sdio_bus 注册了。sdio_register_bus(); //调用到 sdio_bus.c 中去

int sdio_register_bus(void)
{
    return bus_register(&sdio_bus_type);
}
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,
    .pm        = SDIO_PM_OPS_PTR,
};
-----------------------------------------------------------------------

这样,core.c , bus.c , host.c , sdio_bus.c 的内容就大概了解了。


driver/mmc/card/block.c
static int __init mmc_blk_init(void)
{
    int res;

    if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
        pr_info("mmcblk: using %d minors per device\n", perdev_minors);

    max_devices = 256 / perdev_minors;

    res = register_blkdev(MMC_BLOCK_MAJOR, "mmc"); //注册一个块设备,
    if (res)
        goto out;

    res = mmc_register_driver(&mmc_driver);
    if (res)
        goto out2;

    return 0;
 out2:
    unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
 out:
    return res;
}

static void __exit mmc_blk_exit(void)
{
    mmc_unregister_driver(&mmc_driver);
    unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
}

module_init(mmc_blk_init);
module_exit(mmc_blk_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver");
在mmc_blk_init中, register_blkdev(major, "mmc")的作用是注册一个块设备。

register_blkdev的功能比较少,一是动态分配设备号,二是在/proc/devices中创建一个入口项 。故通过它之后,系统还是不能使用块设备的。
接着我们看mmc_register_driver(&mmc_driver); 在/driver/mmc/core/bus.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,
};
/**
 *    mmc_register_driver - register a media driver
 *    @drv: MMC media driver
 */
int mmc_register_driver(struct mmc_driver *drv)
{
    drv->drv.bus = &mmc_bus_type;
    return driver_register(&drv->drv);
}

EXPORT_SYMBOL(mmc_register_driver);

这两句代码比较好理解吧。在注册一个struct driver之前,都要先设置它的bus,类似的,在platform_driver_register中我们也可所以看到:
drv->driver.bus=&platform_bus_type


driver_register将到相应总线mmc_bus_type上去搜索相应设备。找到设备后就设置dev->driver=drv,并调用mmc_bus_type总线的probe函数被调用,即mmmc_bus_probe函数.
我们跟踪driver_register(&drv->drv),它会调应bus_add_driver。

/**
 * driver_register - register driver with bus
 * @drv: driver to register
 *
 * We pass off most of the work to the bus_add_driver() call,
 * since most of the things we have to do deal with the bus
 * structures.
 */
int driver_register(struct device_driver *drv)
{
    int ret;
    struct device_driver *other;

    BUG_ON(!drv->bus->p);

    if ((drv->bus->probe && drv->probe) ||
     (drv->bus->remove && drv->remove) ||
     (drv->bus->shutdown && drv->shutdown))
        printk(KERN_WARNING "Driver '%s' needs updating - please use "
            "bus_type methods\n", drv->name);

    other = driver_find(drv->name, drv->bus);
    if (other) {
        put_driver(other);
        printk(KERN_ERR "Error: Driver '%s' is already registered, "
            "aborting...\n", drv->name);
        return -EBUSY;
    }

    ret = bus_add_driver(drv);
    if (ret)
        return ret;
    ret = driver_add_groups(drv, drv->groups);
    if (ret)
        bus_remove_driver(drv);
    return ret;
}
EXPORT_SYMBOL_GPL(driver_register);
bus_add_driver --- add a driver to the bus.
/**
 * bus_add_driver - Add a driver to the bus.
 * @drv: driver.
 */
int bus_add_driver(struct device_driver *drv)
{
    struct bus_type *bus;
    struct driver_private *priv;
    int error = 0;

    bus = bus_get(drv->bus);
    if (!bus)
        return -EINVAL;

    pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

    priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    if (!priv) {
        error = -ENOMEM;
        goto out_put_bus;
    }
    klist_init(&priv->klist_devices, NULL, NULL);
    priv->driver = drv;
    drv->p = priv;
    priv->kobj.kset = bus->p->drivers_kset;
    error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
                 "%s", drv->name);
    if (error)
        goto out_unregister;

    if (drv->bus->p->drivers_autoprobe) {
        error = driver_attach(drv);
        if (error)
            goto out_unregister;
    }
    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
    module_add_driver(drv->owner, drv);

    error = driver_create_file(drv, &driver_attr_uevent);
    if (error) {
        printk(KERN_ERR "%s: uevent attr (%s) failed\n",
            __func__, drv->name);
    }
    error = driver_add_attrs(bus, drv);
    if (error) {
        /* How the hell do we get out of this pickle? Give up */
        printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
            __func__, drv->name);
    }

    if (!drv->suppress_bind_attrs) {
        error = add_bind_files(drv);
        if (error) {
            /* Ditto */
            printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
                __func__, drv->name);
        }
    }

    kobject_uevent(&priv->kobj, KOBJ_ADD);
    return 0;

out_unregister:
    kobject_put(&priv->kobj);
    kfree(drv->p);
    drv->p = NULL;
out_put_bus:
    bus_put(bus);
    return error;
}

在调用kobject_register之后,我们可以在/sys/bus/mmc/driver目录下看到mmcblk文件driver_attach函数会遍历相应总线(mmc_bus_type)上的dev,对这些dev执行总线的match函数(mmc_bus_match)。如果match成功,它会调用mmc_bus_type总线的probe函数mmc_bus_probe(如果总线的probe存在的话).我们在以上走过的流程中可以看到,我们并没有向总线添加任何设备,故mmc_bus_probe是不会调用的。但相应的driver已经注册到系统了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值