Linux内核大讲堂(一)设备驱动的基石驱动模型(6)
上节我们大概分析了driver_register是怎么工作的。有的细节虽然没有到位,但是记住一句话,贪多嚼不烂,我们首先要建立的是驱动模型在我们脑海中的第一印象。本节将讲述最后两个主角,device和class的创建。讲完这些就会来个大集合。让四大天王(device,class,device_driver,bus)一起登台向各位致敬,表演?想要四大天王表演?没问题,可四大天王不但身份尊贵,最主要的是个个都多才多艺,要完全了解四大天王的技能,肯定不能急罗,得一样一样来欣赏。
好,让我们先忘掉四大天王。先研究一下我们的美女device和人妖class,不好意思说错了,class其实是用来描述device美女的共同特征的。打个大家最喜欢也最容易理解的比方。
你,没错,就是说你,正在看这篇文章的家伙,我知道你喜欢像舒鸿淇这种性感,身材好的。别不承认。但是哥我的品味和你不一样,我喜欢像刘亦菲这种清纯的神仙姐姐。这时候就有两个阵营了。性感派和清纯派,性感派的代表主要有舒淇、钟丽缇、小泽马利亚、苍井空等老师。而清纯派的代表主要有刘亦菲、阿娇(如果大家都不承认那就不算)、高圆圆、杨冥等。你我都知道要美女可以分阵营,凭什么linux内核的作者不懂要分类?这么说如果还不明白分类的好处那我就只能说:哥们,你太单纯了!
行,我再解释一下,在/sys/class/下面有两个目录如下:
/sys/class/性感型美女、/sys/class/清纯型美女
你想找舒淇?那就进/sys/class/性感型美女找。
你想找刘亦菲?那就进/sys/class/清纯型美女找。
明白了吗?class最主要就是为了分类。能分到一类当然会有一些共同的属性,所以类下面的设备都会有一些共同的属性。
有同志说,如果我有特殊爱好喜欢人妖怎么办?自已创建一个人妖类,然后再把人妖扔里面就得了。
怎么创建啊?用什么创建啊?别急,哥马上教你。
就两个函数:
class_register()和device_register(),不过要说一下,我们在之前的介绍一直都是挑一个重要点的函数进行讲解的,并不是说只有这一个函数,linux内核当中会提供很多像create_class之类的函数对其进行封装。
#defineclass_register(class) \
({ \
staticstructlock_class_key__key; \
__class_register(class,&__key); \
})
int__class_register(structclass*cls,structlock_class_key*key)
{
structsubsys_private*cp;
interror;
pr_debug("deviceclass'%s':registering\n",cls->name);
cp=kzalloc(sizeof(*cp),GFP_KERNEL);
if(!cp)
return-ENOMEM;
klist_init(&cp->klist_devices,klist_class_dev_get,klist_class_dev_put);
INIT_LIST_HEAD(&cp->class_interfaces);
kset_init(&cp->glue_dirs);
__mutex_init(&cp->class_mutex,"structclassmutex",key);
error=kobject_set_name(&cp->subsys.kobj,"%s",cls->name);
if(error){
kfree(cp);
returnerror;
}
/*setthedefault/sys/devdirectoryfordevicesofthisclass*/
if(!cls->dev_kobj)
cls->dev_kobj=sysfs_dev_char_kobj;
#ifdefined(CONFIG_BLOCK)
/*lettheblockclassdirectoryshowupintherootofsysfs*/
if(!sysfs_deprecated||cls!=&block_class)
cp->subsys.kobj.kset=class_kset;
#else
cp->subsys.kobj.kset=class_kset;
#endif
cp->subsys.kobj.ktype=&class_ktype;
cp->class=cls;
cls->p=cp;
error=kset_register(&cp->subsys);
if(error){
kfree(cp);
returnerror;
}
error=add_class_attrs(class_get(cls));
class_put(cls);
returnerror;
}
实在是太简单了,哥都不想分析了。本着对菜菜鸟同志负责任的态度,我还是讲一下几个关键的变量初始化的地方吧。
if(!cls->dev_kobj)
cls->dev_kobj=sysfs_dev_char_kobj;
#ifdefined(CONFIG_BLOCK)
/*lettheblockclassdirectoryshowupintherootofsysfs*/
if(!sysfs_deprecated||cls!=&block_class)
cp->subsys.kobj.kset=class_kset;
#else
cp->subsys.kobj.kset=class_kset;
#endif
cp->subsys.kobj.ktype=&class_ktype;
这一堆中的右值除了class_ktype全都是在/drivers/base/init.c的driver_init()中调用对应的子函数来初始化及注册。
下面来看看device_register()。如果看懂了bus_register()和driver_register()。这个应该不会有太大的问题。先给出函数定义:
intdevice_register(structdevice*dev)
{
device_initialize(dev);
returndevice_add(dev);
}
和driver_register太像了,函数命名恰如其份的反应了他的功能。首先初始化dev。然后再将其加到我们的大家庭中。
voiddevice_initialize(structdevice*dev)
{
dev->kobj.kset=devices_kset;
kobject_init(&dev->kobj,&device_ktype);
INIT_LIST_HEAD(&dev->dma_pools);
mutex_init(&dev->mutex);
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_pm_init(dev);
set_dev_node(dev,-1);
}
intdevice_add(structdevice*dev)
{
structdevice*parent=NULL;
structclass_interface*class_intf;
interror=-EINVAL;
dev=get_device(dev);
if(!dev)
gotodone;
if(!dev->p){
error=device_private_init(dev);
if(error)
gotodone;
}
/*
*forstaticallyallocateddevices,whichshouldallbeconverted
*someday,weneedtoinitializethename.Wepreventreadingback
*thename,andforcetheuseofdev_name()
*/
if(dev->init_name){
dev_set_name(dev,"%s",dev->init_name);
dev->init_name=NULL;
}
if(!dev_name(dev)){
error=-EINVAL;
gotoname_error;
}
pr_debug("device:'%s':%s\n",dev_name(dev),__func__);
parent=get_device(dev->parent);
setup_parent(dev,parent);
/*useparentnuma_node*/
if(parent)
set_dev_node(dev,dev_to_node(parent));
/*first,registerwithgenericlayer.*/
/*werequirethenametobesetbefore,andpassNULL*/
error=kobject_add(&dev->kobj,dev->kobj.parent,NULL);
if(error)
gotoError;
/*notifyplatformofdeviceentry*/
if(platform_notify)
platform_notify(dev);
error=device_create_file(dev,&uevent_attr);
if(error)
gotoattrError;
if(MAJOR(dev->devt)){
error=device_create_file(dev,&devt_attr);
if(error)
gotoueventattrError;
error=device_create_sys_dev_entry(dev);
if(error)
gotodevtattrError;
devtmpfs_create_node(dev);
}
error=device_add_class_symlinks(dev);
if(error)
gotoSymlinkError;
error=device_add_attrs(dev);
if(error)
gotoAttrsError;
error=bus_add_device(dev);
if(error)
gotoBusError;
error=dpm_sysfs_add(dev);
if(error)
gotoDPMError;
device_pm_add(dev);
/*Notifyclientsofdeviceaddition.Thiscallmustcome
*afterdpm_sysf_add()andbeforekobject_uevent().
*/
if(dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE,dev);
kobject_uevent(&dev->kobj,KOBJ_ADD);
bus_probe_device(dev);
if(parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
if(dev->class){
mutex_lock(&dev->class->p->class_mutex);
/*tietheclasstothedevice*/
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
/*notifyanyinterfacesthatthedeviceishere*/
list_for_each_entry(class_intf,
&dev->class->p->class_interfaces,node)
if(class_intf->add_dev)
class_intf->add_dev(dev,class_intf);
mutex_unlock(&dev->class->p->class_mutex);
}
done:
put_device(dev);
returnerror;
DPMError:
bus_remove_device(dev);
BusError:
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
if(MAJOR(dev->devt))
devtmpfs_delete_node(dev);
if(MAJOR(dev->devt))
device_remove_sys_dev_entry(dev);
devtattrError:
if(MAJOR(dev->devt))
device_remove_file(dev,&devt_attr);
ueventattrError:
device_remove_file(dev,&uevent_attr);
attrError:
kobject_uevent(&dev->kobj,KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
cleanup_device_parent(dev);
if(parent)
put_device(parent);
name_error:
kfree(dev->p);
dev->p=NULL;
gotodone;
}
函数本身实在找不到有什么需要讲的。
不过有一点比较重要,就是在设备注册的时候,一个设备不能直接从属于一个类和一条总线。太绕了,实例:
structdevicedev;
dev.class=xx;
dev.bus=yy;
device_register(&dev);
这样是不行的,这样会在创建subsystem链接的时候出现重复创建的现象,导致注册失败。其实我们可以这样解决。创建一个父类设备从属于某个class,然后将子类设备的parent指向父类设备地址,再将子类设备的bus指向我的总线的地址。这样就可以解决了。下一节就是四大天王齐登台亮相。^_^
<!--EndFragment-->
分享到:
相关推荐
深入linux设备驱动程序内核机制 pdf深入linux设备驱动程序内核机制深入linux设备驱动程序内核机制深入linux设备驱动程序内核机制深入linux设备驱动程序内核机制深入linux设备驱动程序内核机制深入linux设备驱动程序...
linux内核大讲堂_驱动模型_示例代码_无为和尚
浅谈 Linux 内核开发之 PCI 设备驱动
Linux内核驱动开发详解,基于kernel4.0,对驱动工程师很有帮助
linux内核驱动模型linux内核驱动模型linux内核驱动模型
linux 2.6 内核设备驱动模型精华描述,包括链表、hash链表、kobject等
Linux内核和设备驱动程序实验报告
浅谈 Linux 内核开发之 PCI 设备驱动.pdf
Linux驱动开发:Linux内核模块、字符设备驱动、IO模型、设备树、GPIO子系统、中断子系统、platform总线驱动、I2C总线驱动、SPI总线驱动 Linux项目是一个开放源代码的操作系统项目,由林纳斯·托瓦兹(Linus Torvalds...
《Linux设备驱动开发详解-基于最新的Linux4.0内核》相关源码
Linux内核与设备驱动程序学习资料笔记+源码.zipLinux内核与设备驱动程序学习资料笔记+源码.zipLinux内核与设备驱动程序学习资料笔记+源码.zipLinux内核与设备驱动程序学习资料笔记+源码.zipLinux内核与设备驱动程序...
Linux设备驱动开发详解:基于最新的Linux 4.0内核.pdf Linux设备驱动开发详解:基于最新的Linux 4.0内核.pdf Linux设备驱动开发详解:基于最新的Linux 4.0内核.pdf
Linux内核分析值设备驱动模型,关于设备驱动模型的分析,和内核源代码注释笔记
Linux设备驱动开发详解:基于最新的Linux4.0内核,linux设备驱动开发详解pdf,LINUX源码
预览合集,请购买正版书籍:全部有书签导引,便于查阅 Linux 设备驱动程序开发 精通 [印] Sreekrishnan Venkaveswaran 高清_.pdf Linux 设备驱动 第三版_En_.pdf ...Linux 设备驱动程序内核机制 深入 陈学松著_.pdf
深入理解linux内核英文原版+linux设备驱动开发详解深入理解linux内核英文原版+linux设备驱动开发详解深入理解linux内核英文原版+linux设备驱动开发详解
深入剖析Linux内核与设备驱动 完整版 pdf
Linux内核 内核模型Linux内核 Linux内核 内核模型内核模型Linux内核 内核模型
本文深入探讨了Linux设备驱动程序的内核机制,并提供了一个简单的字符设备驱动程序示例。通过源码示例,详细讲解了驱动程序注册与注销、文件操作函数的实现、设备号分配等关键概念和操作方法。 通过学习本文,您将...
深入剖析Linux内核与设备驱动 [董峰编著].part1.rar