一. device kset 和 bus kset
根据sys的结构图,我们知道,在sysfs中,首先注册的是bus,devices
而buses_init
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
该函数调用
Kset_create(name,uevent_ops,parent_kobj)
和
Kset_register(kset)
Kset_create(name,uevent_ops,parent_kobj)
简单的就是生成一个kset结构,
而Kset_register(kset)
就是将此kset注册(对于kset->kobj->kset为空的则,自己创建为kset头,否则将此kset添加入kset->kobj->kset所指向的kset链表)
Bus 总 kset
static struct kset_uevent_ops bus_uevent_ops = {
.filter = bus_uevent_filter,
};
Device 总 kset
static struct kset_uevent_ops device_uevent_ops = {
.filter = dev_uevent_filter,
.name = dev_uevent_name,
.uevent = dev_uevent,
};
具体生成的bus_kset如下,
我们知道由kobject 决定了层次目录,以此为基础进行分析
二.platform bus 和device 已经目录下driver和device
Platform_bus
Platform_bus_init
---------àdevice_register(&platform_bus); 注册到device目录下
struct device platform_bus = {
.bus_id = "platform",
};
---------------àdevice_initialize(dev)
----------------àdevice_add(dev)
以kobject的层次决定目录,我们可以知道
Platform_bus这一device,其platform_bus->kobj->parent=device_kset->kobj
同时,能通过device_kset->kobj->list遍历整个所有的kobj,实际上与遍历文件一样
同时,根据kset容器的定义,可以知道kset目录下放置的全是device,而实际上这些device->kset=device_kset,
所以可以想象,device目录下的结构图为,
---------àbus_register(&platform_bus_type);注册到bus目录下
由于 bus总线下总是绑定着设备和驱动,这样就有了
bus_type_priveate->klist_devices和bus_type_private->klist_drivers
通过将总线上的device(实际只是链接关系,真正的device在device_kset下)通通添加到bus_type_private->klist_devices上,将总线上的驱动通通添加到bus_type_private->klist_drivers上
其中bus_type_priveate->device_kset对应的bus下的device目录
bus_type_private->driver_kset对应的bus下的driver目录
而显然,由目录关系我们知道
Bus_type_private->subsys->kobj->parent=bus_kset->kobj;
Bus_type_private->subsys->kobj->kset=bus_kset;
同时,能通过bus_kset->kobj->list遍历bus整个所有的kobj,实际上与遍历文件一样
而bus下的device目录(kset->parent=bus_type_private->subsys->kobj)
Driver目录(kset->parent=bus_type_private->kobj)
三.platform_driver_register,platform_device_register
以上两步之后,就等着设备和驱动来注册了
分别调用platform_driver_register,platform_device_register
Platform_device_register
static struct platform_device test_device={
.name="test_ts",
.id=-1;
}
If(!pdev->id!=-1)
Id=-1,表明设备只有一个,用设备名为bus_id,
就是将pdev->name赋值给pdev->dev.bus_id
而ID不为1,表明设备有多个,需要用序号来表明bus_id
pdev->dev.bus_id=pdev->name +‘.’+pdev->id
补充platform中资源的应用
简单的就是将对应资源添加到对应的资源树中
比如,
如果platform的资源r是IORESOUCE_MEM,则其parent resource p则为
Iomem_resouce
如果资源为IORESOUCE_IO,则为ioprot_resource
然后调用insert_resource(p,r)将资源添加到资源树中
struct resource iomem_resource = {
.name = "PCI mem",
.start = 0,
.end = -1,
.flags = IORESOURCE_MEM,
};
struct resource ioport_resource = {
.name = "PCI IO",
.start = 0,
.end = IO_SPACE_LIMIT,
.flags = IORESOURCE_IO,
};
在bus_attach_device中,先是检查属性文件drivers_autoprobe是否为1,
为1,则调用device_attach,
如果返回为大于等于0的值(未匹配成功),则将设备挂在bus的klist_device下,当驱动请求匹配的时候,platform bus总线就会遍历devices链表为驱动查找合适的设备。具体代码就是
Klist_add_tail(&dev->knode_bus,&bus->p->klist_devices)
对于device_attach,就是对所有的driver调用driver_probe_device,查找是否有匹配的driver
一般是先检测drv->bus->match是否存在,存在则调用(platform match 存在),match主要是检查driver和Device的name是否一样,如果一样则调用probe,否则继续查找
接着是检测drv->bus->probe是否存在,存在则调用(platform bus不存在),否则调用drv->probe,而drv->probe又会转而调用具体的platform_driver->probe
platform_driver_register
static struct platform_driver test_driver={
.probe = test_probe,
.remove =test_remove,
.driver={
.name="test_ts",
.owner=THIS_MODULE,
},
}
在platform_driver_register中,如果platform_driver定义了.Probe,
.remove,
则其内嵌的driver的probe和remove也会相应的设置platform对应的函数,从而能够调用platform_driver的probe和remove。
具体就是
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
driver_register(&drv->driver)
然后通过内嵌的driver调用driver_register进行注册
Driver_register的调用流程如下:
(1) 检测总线的操作函数和驱动的操作函数是否同时存在,同时存在,则使用总线提供的操作函数
(2) 检测是否已经注册过
(3) 添加驱动到总线上 bus_add_drv(drv)
(4) 添加驱动到组driver_add_groups(drv,drv->groups)
Bus_add_drv中,
主要在如果driver_autoprobe设置为1,则调用driver_attach(和device_attach类似),并且将驱动添加到klist_drivers中,
Klist_add_tail(&priv->knode_bus,&bus->p->klist_drivers)
Module_add_driver(drv->owner,drv)// 与/sys/modules */ module_kset有关
经过这样之后,platform驱动模型就建立好了
四.文件的操作和ktype
上面说到的autoprobe的属性文件就是ktype提供的一种修改内核参数的方法。
建立属性文件autoprobe的方法不提,一般使用的sysfs_create_file
而通过系统的调用,文件的操作会调用sysfs_file_operation
const struct file_operations sysfs_file_operations = {
.read = sysfs_read_file,
.write = sysfs_write_file,
.llseek = generic_file_llseek,
.open = sysfs_open_file,
.release = sysfs_release,
.poll = sysfs_poll,
};
在sysfs_open_file中,我们通过file找到sysfs_dirent,再由sysfs_dirent找到对应的kobj结构,(文件对应的kobj)
Struct sysfs_buffer *buffer;
Struct sysfs_ops *ops;
在该函数中,
这种重定向ops的方法,在讲ALSA驱动框架时,说到打开/dev/dsp也提到过。
Ktype作用正在于此
If(kobj->ktype&&kobj->ktype->sysfs_ops)
Ops=kobj->ktype->sysfs_ops;
…
然后将设置好的ops挂在在buffer上,
Buffer->ops=ops;
File->private_data=buffer;
而在sysfs_read_file和sysfs_write_file中,通过这种方式,一路跟踪,最后
分别调用了(对于bus的autoprobe文件而言)
Bus_attr->show(bus_priv->bus,buf)
Bus_attr->store(bus_priv->bus,buf)
这里就是在定义属性文件时提到的.show和.Store
总的来说基本上,驱动模型大致是这样,不过其中的class,uevent等没有细说