Linux设备模型.docx
- 文档编号:12925197
- 上传时间:2023-04-22
- 格式:DOCX
- 页数:16
- 大小:21.21KB
Linux设备模型.docx
《Linux设备模型.docx》由会员分享,可在线阅读,更多相关《Linux设备模型.docx(16页珍藏版)》请在冰豆网上搜索。
Linux设备模型
Linux的sysfs文件系统一般mount在/sys目录。
本文主要介绍sysfs文件系统中设备驱动模型的建立过程,内核版本2.6.29。
设备驱动信息主要用来表示设备以及驱动的层次关系,以及处理热插拔等。
/sys中与之相关的数据有:
class代表一类设备,比如mtd、net、tty等等
bus总线,比如PCI、USB、I2C等
device代表一个设备
driver代表一个驱动
以下是一些sysfs中的全局变量:
///sys/class
structkset*class_kset=kset_create_and_add("class",NULL,NULL);
///sys/bus
structkset*bus_kset=kset_create_and_add("bus",&bus_uevent_ops,NULL);
///sys/devices
structkset*devices_kset=kset_create_and_add("devices",&device_uevent_ops,NULL);
1. Class
1.1 class的基本结构
structclass{
constchar*name;
structmodule*owner;
structclass_attribute*class_attrs;
structdevice_attribute*dev_attrs;
structkobject*dev_kobj;
int(*dev_uevent)(structdevice*dev,structkobj_uevent_env*env);
void(*class_release)(structclass*class);
void(*dev_release)(structdevice*dev);
int(*suspend)(structdevice*dev,pm_message_tstate);
int(*resume)(structdevice*dev);
structpm_ops*pm;
structclass_private*p;
};
structclass_private{
structksetclass_subsys;
structlist_headclass_devices;
structlist_headclass_interfaces;
structksetclass_dirs;
structmutexclass_mutex;
structclass*class;
};
class在sysfs中的层次由structclass_private决定。
structclass只是class_private的封装。
structclass_private:
:
class_subsys.kobj.kset=class_kset;//父目录为/sys/class
structclass_private:
:
class_subsys.kobj->name代表这个class在/sys/class中显示的名字
structclass:
:
dev_attrs为设备属性,往class中添加设备的时候,这些属性会自动添加到设备目录下。
1.2 新建class
新建class有两种方法:
静态创建和动态创建。
● 静态创建
staticstructclassi2c_adapter_class={
.owner=THIS_MODULE,
.name="i2c-adapter",
.dev_attrs=i2c_adapter_attrs,
.class_attrs=...
};
intretval=class_register(&i2c_adapter_class);
● 动态创建
i2c_dev_class=class_create(THIS_MODULE,"i2c-dev");
class_create分配申请一块空间给class,然后对name、owner和class_release函数赋值,并最终调用class_register。
class_register
根据structclass的值,设置structclass_private
调用add_class_attrs在class中添加属性。
● classattrs
class的属性最终是在/sys/class/
用户程序可以直接对这些属性进行读写。
如果要静态创建属性,可以在定义class时对.class_attrs域赋值,使其指向要添加的attr数组。
如果要动态创建。
可以通过函数class_create_file添加。
intclass_create_file(structclass*cls,conststructclass_attribute*attr);
如果是动态创建的属性,需要在模块卸载时调用class_remove_file释放。
如果是静态创建的属性,在调用class_unregister时会自动释放。
2. Bus
2.1bus的基本结构
structbus_type{
constchar*name;
structbus_attribute*bus_attrs;
structdevice_attribute*dev_attrs;
structdriver_attribute*drv_attrs;
int(*match)(structdevice*dev,structdevice_driver*drv);
int(*uevent)(structdevice*dev,structkobj_uevent_env*env);
int(*probe)(structdevice*dev);
int(*remove)(structdevice*dev);
void(*shutdown)(structdevice*dev);
int(*suspend)(structdevice*dev,pm_message_tstate);
int(*suspend_late)(structdevice*dev,pm_message_tstate);
int(*resume_early)(structdevice*dev);
int(*resume)(structdevice*dev);
structpm_ext_ops*pm;
structbus_type_private*p;
};
structbus_type_private{
structksetsubsys;
structkset*drivers_kset;
structkset*devices_kset;
structklistklist_devices;
structklistklist_drivers;
structblocking_notifier_headbus_notifier;
unsignedintdrivers_autoprobe:
1;
structbus_type*bus;
};
与class类似,bus在sysfs中的显示由structbus_type_private决定,structbus_type只是一个封装。
structbus_type_private:
:
subsys.kobj代表/sys/bus/
structbus_type_private:
:
subsys.kobj.kset=bus_kset;//默认父目录为/sys/bus/
structbus_type_private:
:
subsys.kobj.ktype=&bus_ktype;//bus的属性操作
structbus_type_private:
:
subsys.kobj.name=
在/sys/bus/目录,每创建成功一个
在drivers和devices子目录下,每新建一个driver,会把structbus_type中的drv_attrs属性赋给那个driver;每创建一个device,会把structbus_type中dev_attrs赋给那个device。
2.2新建bus
structbus_typei2c_bus_type={
.name="i2c",
.dev_attrs=i2c_dev_attrs,
.match=i2c_device_match,
.uevent=i2c_device_uevent,
.probe=i2c_device_probe,
.remove=i2c_device_remove,
.shutdown=i2c_device_shutdown,
.suspend=i2c_device_suspend,
.resume=i2c_device_resume,
.bus_attr=…
};
intret=bus_register(&i2c_bus_type);
intbus_register(structbus_type*bus);
分配内存给structbus_type_private;
根据structbus_type的域设置bus_type_private;
根据.bus_attr设置bus的属性,这些属性在bus_unregister时会被自动释放。
(bus属性也可通过bus_create_file动态添加,但所有动态添加的属性都要在卸载时通过bus_remove_file释放。
)
3.Device
3.1Device的基本结构
structdevice{
structklistklist_children;
structklist_nodeknode_parent;/*nodeinsiblinglist*/
structklist_nodeknode_driver;
structklist_nodeknode_bus;
structdevice*parent;
structkobjectkobj;
charbus_id[BUS_ID_SIZE];/*positiononparentbus*/
constchar*init_name;/*initialnameofthedevice*/
structdevice_type*type;
unsigneduevent_suppress:
1;
structsemaphoresem;/*semaphoretosynchronizecallstoitsdriver.*/
structbus_type*bus;/*typeofbusdeviceison*/
structdevice_driver*driver;/*whichdriverhasallocatedthisdevice*/
void*driver_data;/*dataprivatetothedriver*/
void*platform_data;/*Platformspecificdata,devicecoredoesn'ttouchit*/
structdev_pm_infopower;
u64*dma_mask;/*dmamask(ifdma'abledevice)*/
u64coherent_dma_mask;/*Likedma_mask,butforalloc_coherentmappingsasnotallhardwaresupports64bitaddressesforconsistentallocationssuchdescriptors.*/
structdevice_dma_parameters*dma_parms;
structlist_headdma_pools;/*dmapools(ifdma'ble)*/
structdma_coherent_mem*dma_mem;/*internalforcoherentmem
override*/
/*archspecificadditions*/
structdev_archdataarchdata;
spinlock_tdevres_lock;
structlist_headdevres_head;
structlist_headnode;
structclass*class;
dev_tdevt;/*dev_t,createsthesysfs"dev"*/
structattribute_group**groups;/*optionalgroups*/
void(*release)(structdevice*dev);
};
3.2device_register
intdevice_register(structdevice*dev) ;
此函数将device登记到sysfs中。
在调用之前,需对structdevice进行初始化。
structdevicedev ;
dev.parent=
dev.release=
dev.class=
dev.bus=
然后调用device_register(&dev) ;
intdevice_register(structdevice*dev)
{
device_initialize(dev);
returndevice_add(dev);
}
device_initialize
做一些初始化工作,dev->kobj.kset=devices_kset ;//代表/sys/device目录
device_add
//设置dev->kobj.parent,即确定这个dev的父目录,详情见下节
setup_parent(dev,dev->parent);
//将dev挂到dev->kobj.parent代表的目录,如果没有parent,父目录默认被设置成dev->kobj.kset代表的目录
kobject_add(&dev->kobj,dev->kobj.parent,"%s",dev->bus_id);
device_create_file(dev,&uevent_attr);//给设备添加uevent属性
if(MAJOR(dev->devt))
device_create_file(dev,&devt_attr);//给设备添加dev属性(打印主从设备号)
device_create_sys_dev_entry(dev);//在设备的class下创建设备链接,比如/sys/char和/sys/block,链接名字为major:
minor;如果设备没有class,默认为/sys/char目录
device_add_class_symlinks(dev);
//在设备目录下建立subsystem链接,指向其所属的class
sysfs_create_link(&dev->kobj,&dev->class->p->class_subsys.kobj,"subsystem");
//在设备所属class目录下建立指向设备的链接,以设备名命名
sysfs_create_link(&dev->class->p->class_subsys.kobj,&dev->kobj,dev->bus_id);
//如果父设备存在,在设备目录下建立指向父设备的链接,以device命名
sysfs_create_link(&dev->kobj,&dev->parent->kobj,"device");
device_add_attrs(dev);
//如果有class,把class中dev_attrs属性都加上
device_add_attributes(dev,class->dev_attrs);
//如果有type,把type中dev_attrs属性都加上
device_add_groups(dev,type->groups);
//把device中groups指向的属性都加上
device_add_groups(dev,dev->groups);
bus_add_device(dev);//在设备有bus时有效
//如果有bus,将bus中dev_attrs都加上
device_add_attrs(bus_get(dev->bus),dev);
//在bus中建立指向设备的链接,以设备名命名
sysfs_create_link(&bus->p->devices_kset->kobj,&dev->kobj,dev->bus_id);
//在设备中建立指向总线的链接,以”subsystem”命名
sysfs_create_link(&dev->kobj,&dev->bus->p->subsys.kobj,"subsystem");
dpm_sysfs_add(dev);//建立power属性
device_pm_add(dev);
kobject_uevent(&dev->kobj,KOBJ_ADD);
bus_attach_device(dev);
/*如果总线支持自动检测设备(drivers_autoprobe==1;默认都支持),调用device_attach(dev);device_attach中,如果发现dev已经有driver与之关联,作一些sysfs的操作;如果没有,对总线中每一个驱动调用__device_attach。
__device_attachdriver_probe_device
driver_probe_device先调用bus的match函数,如果返回的是match,再调用really_probe。
(bus的match函数在这里调用)
really_probe先看bus有没有probe函数,如果有,调用bus的probe。
如果没有,调用driver的probe函数(这里就是我们驱动程序的probe函数被调用的地方)。
*/
if(bus->p->drivers_autoprobe)
ret=device_attach(dev);
klist_add_tail(&dev->knode_parent,&parent->klist_children);
list_add_tail(&dev->node,&dev->class->p->class_devices);
list_for_each_entry(class_intf,&dev->class->p->class_interfaces,node)
if(class_intf->add_dev)
class_intf->add_dev(dev,class_intf);
3.3device的四种类型
在sysfs的设备模型中,有四种设备类型:
物理设备有parent设备,没有class
直接虚拟设备有parent设备和class,parent没有class
间接虚拟设备有parent设备和class,parent有class
纯虚拟设备没有parent,有class(网络环回设备等)
以挂在PCI总线上的I2C适配器为例,首先需要创建一个设备,使其bus域指向PCIbus,这是一个物理设备;然后,以这个物理设备为父设备,创建一个class为I2C_adapter_class的子设备,这个设备是直接虚拟设备,描述I2Cadapter的功能。
I2C子系统对每一个I2Cadapter,又进一步创建了一个字符设备,I2Cdev,这个字符设备的class被设置为I2C_device_class,这里I2Cdev就是一个间接虚拟设备。
除非是纯虚设备,否则任何一个虚拟设备向父设备追溯,一定能找到一个物理设备。
structdevice中有两个域,bus和class,这两个域不能同时有值。
如果bus域非空,说明这个structdevice是挂在某个总线上,那么它必须是一个物理设备,class域必须是NULL;如果class域非空,说明这是一个属于某个类的虚拟设备,那么它的bus域就必须是NULL。
所以,在上节提到的两个函数,device_add_class_symlinks(dev)与bus_add_device(dev)中,虽然都创建了subsystem链接,但它们只有一个会起作用,否则系统会崩溃。
setup_parent函数
voidsetup_parent(structdevice*dev,structdevice*parent) ;
这个函数用来决定dev被加到sysfs的哪个目录下。
代码逻辑为:
kobj=get_device_parent(dev,parent);
if(kobj)
dev->kobj.parent=kobj;
staticstructkobject*get_device_parent(structdevice*dev,structdevice*parent) ;
这个函数会按照设备类型决定设备的父目录 :
● 如果是物理设备且有父设备(dev->class==NULL&&dev->parent)
父目录就是父设备代表的目录
● 如果是直接虚拟设备(dev->class&&dev->parent&&dev->parent->class!
=NULL)
在父设备代表的目录下新建一个子目录,名字为dev->class->name。
然后把这个新建的目
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 设备 模型