linux设备驱动——andriod平台wlan驱动
struct klist klist_children;
struct klist_node knode_parent; /* node in sibling list */
struct klist_node knode_driver;
struct klist_node knode_bus;
struct device *parent;
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
struct device_type *type;
unsigned is_registered:1;
unsigned uevent_suppress:1;
* its driver.
*/
struct device_driver *driver; /* which driver has allocated this device */ //这个设备挂接的驱动
void *driver_data; /* data private to the driver */
void *platform_data; /* Platform specific data, device core doesn't touch it */
struct dev_pm_info power;
int numa_node; /* NUMA node this device is close to */
#endif
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
/* arch specific additions */
struct dev_archdata archdata;
struct list_head devres_head;
struct list_head node;
struct class *class;
dev_t devt; /* dev_t, creates the sysfs "dev" */
struct attribute_group **groups; /* optional groups */
};
const char * name; //设备驱动的名字
struct bus_type * bus; //设备驱动挂接的总线的类型
struct klist klist_devices; //这个驱动对应的设备的链表
struct klist_node knode_bus;
const char * mod_name; /* used for built-in modules */
struct module_kobject * mkobj;
int (*remove) (struct device * dev);
void (*shutdown) (struct device * dev);
int (*suspend) (struct device * dev, pm_message_t state);
int (*resume) (struct device * dev);
};
const char * name; //总线的名字
struct module * owner;
struct kset drivers; //挂接在该总线上的驱动的集合
struct kset devices; //挂接在该总线上的设备的集合
struct klist klist_devices;
struct klist klist_drivers;
struct device_attribute * dev_attrs; //设备属性
struct driver_attribute * drv_attrs; //驱动属性
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device * dev);
int (*remove)(struct device * dev);
void (*shutdown)(struct device * dev);
int (*suspend_late)(struct device * dev, pm_message_t state);
int (*resume_early)(struct device * dev);
int (*resume)(struct device * dev);
};
贴一下sdio_register_driver函数:
int sdio_register_driver(struct sdio_driver *drv)
{
drv->drv.name = drv->name; //首先忽略下面两行,直接进入driver_register函数.
drv->drv.bus = &sdio_bus_type; //实际上这行代码是关键,而下面的函数中我们要找的仅仅是调用probe函数的地方而已,稍后分析
return driver_register(&drv->drv);
}
来看driver_register函数的内容,由于其中涉及较多有关klist、kobject结构的内容,如果有不明白的地方,请查看同为《linux设备驱动——andriod平台wlan驱动》系列的介绍klist、kobject内容的章节,这里希望有的放矢.它的代码在drivers/base/driver.c中.
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
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); //在kobject结构组成的链表中查找是否已经存在这个驱动,以前的blog讲过,驱动必然挂接在某个总线上.请复习,返回值是device_driver结构的指针
if (other) {
put_driver(other); //由于之前增加了引用计数,这里在减1
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting.../n", drv->name);
return -EEXIST;
}
ret = bus_add_driver(drv); //此函数是重点!
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups); //这两个函数不用介绍也猜的出来
if (ret)
bus_remove_driver(drv);
return ret;
}
那么我们接着看bus_add_driver函数,这个函数的作用是:如果驱动还未挂接在总线上,挂接它并且调用probe函数进行探测.它的代码在drivers/base/bus.c中.
int bus_add_driver(struct device_driver *drv) //这个函数我不记得我的blog中是否有讲过(我记得我写过注释,如果没写过,就请自己看吧 呵呵 )
{
.......................................
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv); //这个函数是重点.
if (error)
goto out_unregister;
}
......................................
}
driver_attach函数在drivers/base/dd.c中,很简单的一句话:
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
这个函数会调用__driver_attach函数,我们已经接近目标了.
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem);
if (!dev->driver)
driver_probe_device(drv, dev); //此函数就是我们要找的函数了.
up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
return 0;
}
driver_probe_device函数中有一个really_probe函数,这是我们的最终目的地:
static int really_probe(struct device *dev, struct device_driver *drv)
{
...................................................
if (dev->bus->probe) { //我们看到了这里会调用probe函数,同样这里也会决定你的probe函数是使用一个参数还是两个参数,
ret = dev->bus->probe(dev); 因为这取决于你是如何注册的.也就是是否调用了(仅仅针对于网友的提问)platform_device_register
if (ret) 函数来注册设备.具体看下面的总结.
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
....................................................
}
好了,这里来总结一下得失,同时根据以上列的代码回答网友的提问: if (dev->bus->probe)这个表示是否注册了device结构,如果注册了并且给device结构挂接上了驱动和总线,那么调用挂接在device结构中的总线的probe函数.这里的device结构从哪里冒出来的?它在 bus_for_each_dev函数中:
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device *dev;
int error = 0;
if (!bus)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->knode_bus : NULL));
while ((dev = next_device(&i)) && !error) //查找每个挂接在sdio总线上的设备,看他们是否有注册,并调用相应的probe函数也就是
error = fn(dev, data); __driver_attach函数.实际上就是查找device结构.
klist_iter_exit(&i);
return error;
}
关于platform_device结构与device结构的关系,就不用我在解释了吧!probe函数的调用,就取决于你在你注册的device结构中挂接的总线的类型了.因为调用 dev->bus->probe(dev); 所以清查看一下你注册是挂接的总线的probe函数的参数即可. 一般来说,参数会是两个,因为一类总线上总是可以挂接多个设备,所以我们还需要一个device_id. 如果行到else部分: else if (drv->probe) .这里调用驱动的probe函数,由于我们注册的是sdio_driver结构.看一看sdio_driver结构的申明,在include/linux/mmc/sdio_func.h中:
struct sdio_driver {
char *name;
const struct sdio_device_id *id_table;
int (*probe)(struct sdio_func *, const struct sdio_device_id *); //很显然probe就是两个参数,而不是一个.
void (*remove)(struct sdio_func *);
struct device_driver drv;
};
至于为什么你的probe是一个参数,我希望你能给出完整的注册流程才能分析,但是我认为根据我刚才分析的流程应该可以自己找出相应的代码了.
我该怎么样来形容这个图呢? 它把依赖关系说的已经足够清楚了!!我这里唯一要解释的仅仅是在驱动文件夹被正确后所谓的文件属性有在那里。同样我们顺着目录/sys/bus/sdio/driver/wlan_sdio/下我们发现了bind、unbind和new_id三个文件。
好了,别忘了我们在前面提过的问题,kset是如何扮演容器的角色的呢?图中很清楚吧,看看粉红色的箭头,kset children list用来将将同类型的kobject连接起来以达到容器的效果。
但是我们的驱动还没有结束,关于这个图的建立,我们势必要用代码才能说得明白。
我们还是花费一点时间来看一下内核中的代码,关于sdio总线注册的代码部分,其它的部分,大家类举就可以了。懂了这段自然就懂了全部:
int bus_register(struct bus_type * bus)
{
int retval;
BLOCKING_INIT_NOTIFIER_HEAD(&bus->bus_notifier);
retval = kobject_set_name(&bus->subsys.kobj, "%s", bus->name); //总线的名字”sdio”,我们说过了一个kobject对应一个目录,这时会为这个目录赋值名字。
if (retval)
goto out;
bus->subsys.kobj.kset = &bus_subsys; //将其kset指向bus_subsys.,如何理解? 看看ldd_bus_type指向bus_subsys的那条蓝线
retval = subsystem_register(&bus->subsys); //bus->subsys的注册,实际上是用kset指针将其链接在一起。好吧 我得承认实际上这里取消了subsysem结构的概念,用kset代替了。这里会创建一个目录。它是一个kset也是一个kobject,因为kset包含了kobject。
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_uevent); //创建属性文件
if (retval)
goto bus_uevent_fail;
kobject_set_name(&bus->devices.kobj, "devices"); //设置devices kset的名字为devices
bus->devices.kobj.parent = &bus->subsys.kobj; //参见ldd_bus_type->devices指向ldd_bus_type->sub_sys的红色箭头(注意这里也不是subsystem,而是kset)
retval = kset_register(&bus->devices); //创建devices命名的目录
if (retval)
goto bus_devices_fail;
kobject_set_name(&bus->drivers.kobj, "drivers"); //设置devices kset的名字为drivers
bus->drivers.kobj.parent = &bus->subsys.kobj; //同样参见ldd_bus_type->drivers指向ldd_bus_type->sub_sys的红色箭头(注意这里也不是subsystem,而是kset)。
bus->drivers.ktype = &driver_ktype; //对kobject默认属性的赋值
retval = kset_register(&bus->drivers); //创建drivers命名的目录
if (retval)
goto bus_drivers_fail;
klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put); //klist结构的初始化,关于klist链接的作用我们已经说得很清楚了
klist_init(&bus->klist_drivers, NULL, NULL);
bus->drivers_autoprobe = 1;
retval = add_probe_files(bus); //添加探测属性
if (retval)
goto bus_probe_files_fail;
retval = bus_add_attrs(bus); //添加其他属性
if (retval)
goto bus_attrs_fail;
pr_debug("bus type '%s' registered/n", bus->name);
return 0;
bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(&bus->drivers);
bus_drivers_fail:
kset_unregister(&bus->devices);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
subsystem_unregister(&bus->subsys);
out:
return retval;
}
至此 我们终于理顺了整个过程