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

平平淡淡才是真

Linux内核大讲堂之设备驱动的基石驱动模型(6)

相关文章:

你感兴趣的文章:

标签云: