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;
//kobject结构,关于这个结构与kset结构以及subsystem结构,笔记中会有描述。
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
struct device_type *type;
unsigned is_registered:1;
unsigned uevent_suppress:1;
to
* 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. */
/* internal for coherent mem override */
/* 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;
//设备驱动挂接的总线的类型
//kobject结构
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;
//与该总线相关的subsystem
struct kset drivers; //挂接在该总线上的驱动的集合
struct kset devices;
//挂接在该总线上的设备的集合
struct klist klist_devices;
struct klist klist_drivers;
//总线属性
struct device_attribute * dev_attrs; //设备属性
struct driver_attribute * drv_attrs;
//驱动属性
struct device_driver * drv);
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);
};
设备模型章节)
首先不管是设备还是驱动,都是挂接在某条总线上的,也就是说我们根据总线类型的不同来区分各种设备和驱动。(但是我们也要注意到,一个设备和驱动是可以挂接在不同的总线上的,比如网卡可以挂接在pci和sdio总线上,但这并不是说在linux设备模型中就可以同时挂接在两个总线上,我们只能选择其中的一种挂接)。
在内核代码中我们找到了bus_type结构,我们发现了它使用了三个kset结构,分别是:
struct kset subsys 、struct kset drivers
、struct kset devices。我们先抛开subsys因为它是用来向上挂接的。这里我们先看drivers与devices两个kset结构的意义。我们从发现一个设备或者驱动说起吧,一个设备或者驱动向内核注册的时候(对于设备来说就是被插入了;对于驱动来说就是.ko模块被加载了),对于每一次设备注册或者驱动注册,我们都得分配一个device结构或者device_drive结构,每一次我们都需要将device结构挂入drivers或devices(kset结构)链表中,这样我们能通过总线找到挂接在这个总线上的所有设备和驱动。但是这里我们注意到仅仅将设备们和驱动们挂接在总线上并不能表明设备和驱动之间的关系,这样的处理仅仅表明了驱动、设备与总线的关系,它们申明了我现在挂接在这条总线下,以后操作我就请通过这条总线。
device_driver *driver这样的声明,而在device_drive中却并没有同样的包含device结构。我们这样想就明白了:对于一个设备来说,我们只能绑定一个驱动;而对于一个驱动来说,我们是可以对应于多个设备的。
也就是说这里device中的driver指针将会指向其绑定的驱动。那么回到probe探测函数,在我们对一个设备驱动进行注册的过程中,我们会在其相应的总线(也就是其挂接的总线)上发出一个探测,这个探测会搜寻所有挂接在这个总线上的尚未被绑定的设备(也就是driver指针为NULL),然后将driver指针指向这个驱动的结构,同时将这个设备的device结构挂接在device_driver结构中的klist链表中。
另外要提及一点就是我居然发现在device结构中也发现了一个这样的结构struct
klist_node knode_driver,它看起来跟klist有关,但是我得说我确实不认为device需要一个klist来挂上它能使用的driver。同样,当一个设备被注册时,它也会去寻找挂接在同一条总线上的驱动,并将自己与这个驱动联系起来。
函数被调用从而注册sdio驱动.这里已经进入内核部分代码,他存在于内核的drivers/mmc/core/sdio_bus.c文件中,噢,忘了说了,我看的内核代码版本是2.6.30.1.
贴一下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))
"bus_type methods/n", drv->name);
other = driver_find(drv->name, drv->bus);
//在kobject结构组成的链表中查找是否已经存在这个驱动,以前的blog讲过,驱动必然挂接在某个总线上.请复习,返回值是
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;
}
那么我们接着看
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);
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是一个参数,我希望你能给出完整的注册流程才能分析,但是我认为根据我刚才分析的流程应该可以自己找出相应的代码了.
太抽象了,能举个例子吗?是的,比如我们的设备树下的pci总线目录/sys/bus/pci下挂接着很多的设备和驱动(当然有很多的设备和驱动可以挂着在pci总线下),那么我们如何将它们联系在一起呢?是的,是通过kset结构,那么kset结构是个什么样的地位呢?噢,我想之前我理解错了,它不是/sys/bus/pci目录,也不是/sys/bus/pci/drivers目录中的某个具体驱动,它刚好就是/sys/bus/pci/drivers目录。不相信?
那么打开你们的linux操作系统,看一看是否每一条总线都有着drives和devices两个目录?它们两个都是嵌入了kset结构;那么/sys/bus/pci/drivers目录中的某个具体驱动呢?它就是嵌入了kobject结构的目录;/sys/bus/pci目录呢?猜对了,它就是subsystem结构的嵌入。
还有要补充的吗?是呢,现在subsystem貌似被取消了,取代它的就是kset结构。这样也就是说一系列的kset就组成了subsystem。
好了,现在我们得费劲心思的捋一遍我们的驱动注册代码,以便找到设备树添加的关键部分。我想我又得强调一下,我的介绍是SDIO驱动,所以请大家看着linux内核代码drivers/mmc中关于sdio的驱动来理解我下面的笔记中的内容(想不看内核代码就理解设备树,我想太难太难)
有关sbi_register函数(这是在我的wlan驱动代码中的函数,并不需要你太多的关注)中sdio_register_driver函数(从现在开始就都是内核函数了)注册驱动的介绍,在sdio_register_driver中将会指明驱动的名称(这里是”wlan_sdio”),此函数的参数为sdio_driver结构。驱动所挂接的总线sdio_bus_type,其结构类型为:
static struct bus_type sdio_bus_type = {
.name = "sdio",
//总线类型
//属性
//ops
driver_register将会完成挂接驱动至总线及生成设备树的过程,其完成的任务大致包括:
1、设置设备驱动中kobject的名字
= &bus->drivers;想象一下我们折腾了那么长时间的kobject与kset的意义,是的,这里就是将driver的kobj所属的kset挂接上总线的kset