Linux设备驱动模型.docx
- 文档编号:8435643
- 上传时间:2023-01-31
- 格式:DOCX
- 页数:23
- 大小:522.94KB
Linux设备驱动模型.docx
《Linux设备驱动模型.docx》由会员分享,可在线阅读,更多相关《Linux设备驱动模型.docx(23页珍藏版)》请在冰豆网上搜索。
Linux设备驱动模型
简介
作者:
hjlin
内核版本:
2.6.29
设备驱动模型框架是linux驱动编程的基础。
它通过kobject,kset,ktype等底层数据结构将bus_type,device,device_driver等高层数据结构组织起来,形成一个层次、分类清晰的驱动模型。
优点如下:
1.代码重用。
将对象抽象为总线、驱动、设备三种,各司其职。
同一总线的多个驱动使用相同的总线对象。
同一驱动可以关联驱动多个设备。
2.通过sysfs文件系统,清晰了展示内核驱动模型中的层次关系。
同时sysfs文件系统还提供了方便的同用户控件交互的接口。
框架
数据结构
Kobject
Kobject是代表驱动模型中的一个对象。
总线、驱动、设备都继承了它。
(在结构体中包含kobject)。
每个kobject在sysfs中表现为一个目录。
每个kobject都有一个parentkobject和所属的kset。
Kset就是kobject所属的kset,通过kset的链表可以找到所有属于它的kobject。
这些kobject进行uevent操作时,都会调用所属的kset的uevent_ops方法。
父kobj,用于表示kobject之间或者kobject和kset,kset之间的在sysfs中的目录结构关系。
如果父kobj不存在,并且所属的kset存在的话,则父kobj就是设置为所属的kset的内嵌kobj。
因此,注册设备、驱动或者总线的时候如果不指定parentkobj的话,父kobj则会设置为所属的kset的kobj。
(todo:
最好画图表示关系)
structkobject{
constchar*k_name;
structkrefkref;
structlist_headentry;//连入到所属的kset的list
structkobject*parent;//父kobj,如果没有父kobj,则使用所属的kset内嵌kobj
structkset*kset;//所属的kset(如果有的话)
structkobj_type*ktype;//所属的ktype,如果所属的kset也有ktype,则用kset的ktype。
structsysfs_dirent*sd;
};
Kest
通过kset可以将kobject组织成一颗层次树。
structkset{
structlist_headlist;//属于该kset的kobj链表
spinlock_tlist_lock;
structkobjectkobj;//内嵌的kobject
structkset_uevent_ops*uevent_ops;//uevent方法,对所有该kset下的kobj进行uevent操作时的方法。
};
structkset_uevent_ops{
int(*filter)(structkset*kset,structkobject*kobj);
constchar*(*name)(structkset*kset,structkobject*kobj);
int(*uevent)(structkset*kset,structkobject*kobj,
structkobj_uevent_env*env);
};
kobj_type
structkobj_type{
void(*release)(structkobject*kobj);//kobject释放时调用
structsysfs_ops*sysfs_ops;//操纵默认属性的读写方法
structattribute**default_attrs;//相应的kobject(kset)的默认属性。
对应于sysfs下的文件。
};
structsysfs_ops{
ssize_t(*show)(structkobject*,structattribute*,char*);
ssize_t(*store)(structkobject*,structattribute*,constchar*,size_t);
};
structattribute{
constchar*name;
structmodule*owner;
mode_tmode;
};
bus_type
代表一个总线。
对应/sys/bus下的一个目录。
管理相应总线下的所有驱动和设备。
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);//uevent方法
int(*probe)(structdevice*dev);//match成功之后会调用。
一般该probe方法内部会调用相应的device_driver的probe方法
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);
structdev_pm_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;
};
Device
structdevice{
structdevice*parent;//父设备
structdevice_private*p;
structkobjectkobj;
charbus_id[BUS_ID_SIZE];
constchar*init_name;/*initialnameofthedevice*/
structdevice_type*type;
structsemaphoresem;/*semaphoretosynchronizecallsto
*itsdriver.
*/
structbus_type*bus;/*typeofbusdeviceison*/
structdevice_driver*driver;/*whichdriverhasallocatedthis
device*/
void*platform_data;/*Platformspecificdata,device
coredoesn'ttouchit*/
structdev_pm_infopower;
#ifdefCONFIG_NUMA
intnuma_node;/*NUMAnodethisdeviceiscloseto*/
#endif
u64*dma_mask;/*dmamask(ifdma'abledevice)*/
u64coherent_dma_mask;/*Likedma_mask,butfor
alloc_coherentmappingsas
notallhardwaresupports
64bitaddressesforconsistent
allocationssuchdescriptors.*/
structdevice_dma_parameters*dma_parms;
structlist_headdma_pools;/*dmapools(ifdma'ble)*/
structdma_coherent_mem*dma_mem;/*internalforcoherentmem
override*/
/*archspecificadditions*/
structdev_archdataarchdata;
dev_tdevt;/*dev_t,createsthesysfs"dev"*/
spinlock_tdevres_lock;
structlist_headdevres_head;
structklist_nodeknode_class;
structclass*class;
conststructattribute_group**groups;/*optionalgroups*/
void(*release)(structdevice*dev);
};
structdevice_private{
structklistklist_children;//子设备的链表
structklist_nodeknode_parent;//作为父设备的字设备链表(klist_children)的一个节点
structklist_nodeknode_driver;//作为所属驱动的设备链表(klist_devices)的一个节点
structklist_nodeknode_bus; //作为所属总线的设备链表(klist_devices)的一个节点
void*driver_data;
structdevice*device;
};
device_driver
structdevice_driver{
constchar*name;
structbus_type*bus;
structmodule*owner;
constchar*mod_name;/*usedforbuilt-inmodules*/
int(*probe)(structdevice*dev);
int(*remove)(structdevice*dev);
void(*shutdown)(structdevice*dev);
int(*suspend)(structdevice*dev,pm_message_tstate);
int(*resume)(structdevice*dev);
structattribute_group**groups;
structdev_pm_ops*pm;
structdriver_private*p;
};
structdriver_private{
structkobjectkobj;
structklistklist_devices;//所驱动的设备的链表
structklist_nodeknode_bus;//作为所属总线的驱动链表(klist_drivers)的一个节点
structmodule_kobject*mkobj;
structdevice_driver*driver;
};
基础方法
kobject_add_internal
staticintkobject_add_internal(structkobject*kobj)
将kobject添加到设备驱动模型中。
主要设置父kobj以及将自己添加到所属的kset的list中,并且在sysfs中创建目录。
kobject_uevent
intkobject_uevent(structkobject*kobj,enumkobject_actionaction)
sendanueventtouserspace
核心方法
注册总线
bus_register(structbus_type*bus)
注册一个总线到驱动模型中。
在sysfs中的结构是:
/sys/bus/platform/
|--devices
||--…
|--drivers
||--…
|--drivers_autoprobe
|--drivers_probe
`--uevent
intbus_register(structbus_type*bus)
{
intretval;
structbus_type_private*priv;
//初始化private数据结构
priv=kzalloc(sizeof(structbus_type_private),GFP_KERNEL);
if(!
priv)
return-ENOMEM;
priv->bus=bus;
bus->p=priv;
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
//设置kobject名称。
即/bus/sys/下的目录名称
retval=kobject_set_name(&priv->subsys.kobj,"%s",bus->name);
if(retval)
gotoout;
priv->subsys.kobj.kset=bus_kset;
priv->subsys.kobj.ktype=&bus_ktype;
priv->drivers_autoprobe=1;
//注册内嵌kset
retval=kset_register(&priv->subsys);
if(retval)
gotoout;
retval=bus_create_file(bus,&bus_attr_uevent);
if(retval)
gotobus_uevent_fail;
//创建设备kset并且注册
priv->devices_kset=kset_create_and_add("devices",NULL,
&priv->subsys.kobj);
if(!
priv->devices_kset){
retval=-ENOMEM;
gotobus_devices_fail;
}
//创建驱动kset并且注册
priv->drivers_kset=kset_create_and_add("drivers",NULL,
&priv->subsys.kobj);
if(!
priv->drivers_kset){
retval=-ENOMEM;
gotobus_drivers_fail;
}
klist_init(&priv->klist_devices,klist_devices_get,klist_devices_put);
klist_init(&priv->klist_drivers,NULL,NULL);
retval=add_probe_files(bus);
if(retval)
gotobus_probe_files_fail;
retval=bus_add_attrs(bus);
if(retval)
gotobus_attrs_fail;
pr_debug("bus:
'%s':
registered\n",bus->name);
return0;
。
。
。
out:
returnretval;
}
注册设备
主要有两个工作,初始化设备,将设备挂接到设备驱动模型中并且处理uevent事件和自动匹配设备驱动。
在platform.c的platform_bus_init方法中使用了device_register(&platform_bus),注册了一个设备,该设备的sys目录为/sys/devices/platform/。
但是一般情况下,内核对于不同的总线会提供不同的封装方法在内部调用device_register来注册设备。
比如说platform_register_device,并且在platform_register_device方法中设置了device的父kobj为platform_bus,导致所有通过的platform_register_device注册的设备的目录都会在/sys/devices/platform/下。
在sys文件系统中的结构是:
todo
intdevice_register(structdevice*dev)
{
device_initialize(dev);
returndevice_add(dev);
}
voiddevice_initialize(structdevice*dev)
{
kobj_set_kset_s(dev,devices_subsys);
kobject_init(&dev->kobj);
klist_init(&dev->klist_children,klist_children_get,
klist_children_put);
INIT_LIST_HEAD(&dev->dma_pools);
INIT_LIST_HEAD(&dev->node);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev,0);
set_dev_node(dev,-1);
}
intdevice_add(structdevice*dev)
{
structdevice*parent=NULL;
structclass_interface*class_intf;
interror=-EINVAL;
dev=get_device(dev);
if(!
dev||!
strlen(dev->bus_id))
gotoError;
pr_debug("DEV:
registeringdevice:
ID='%s'\n",dev->bus_id);
parent=get_device(dev->parent);
error=setup_parent(dev,parent);
if(error)
gotoError;
/*first,registerwithgenericlayer.*/
kobject_set_name(&dev->kobj,"%s",dev->bus_id);
error=kobject_add(&dev->kobj);
if(error)
gotoError;
/*notifyplatformofdeviceentry*/
if(platform_notify)
platform_notify(dev);
/*notifyclientsofdeviceentry(newway)*/
if(dev->bus)
blocking_notifier_call_chain(&dev->bus->bus_notifier,
BUS_NOTIFY_ADD_DEVICE,dev);
//创建uevent属性文件。
可以通过该文件发送事件
error=device_create_file(dev,&uevent_attr);
if(error)
gotoattrError;
//创建dev的属性文件。
if(MAJOR(dev->devt)){
error=device_create_file(dev,&devt_attr);
if(error)
gotoueventattrError;
}
error=device_add_class_symlinks(dev);
if(error)
gotoSymlinkError;
//创建所属class,kobject_type,以及device本身自带的属性文件
error=device_add_attrs(dev);
if(error)
gotoAttrsError;
//创建电源管理子目录power
error=dpm_sysfs_add(dev);
if(error)
gotoPMError;
//将该设备挂接到电源链表下
device_pm_add(dev);
//将该dev添加到bus
error=bus_add_device(dev);
if(error)
gotoBusError;
kobject_uevent(&dev->kobj,KOBJ_ADD);//发送一个uevent事件到用户空间,实现热插拔
bus_attach_device(dev);//自动匹配设备和驱动
if(parent)
klist_add_tail(&dev->knode_parent,&parent->klist_children);
if(dev->class){
down(&dev
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 设备 驱动 模型