上节我们大概分析了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指向我的总线的地址。这样就可以解决了。下一节就是四大天王齐登台亮相。^_^
平平淡淡才是真