现在的位置: 首页 > 综合 > 正文

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

2013年10月04日 ⁄ 综合 ⁄ 共 5309字 ⁄ 字号 评论关闭

转自:无为和尚的Linux内核大讲堂系列。http://blog.csdn.net/z2007b/article/details/6388753

上节我们大概分析了driver_register是怎么工作的。有的细节虽然没有到位,但是记住一句话,贪多嚼不烂,我们首先要建立的是驱动模型在我们脑海中的第一印象。本节将讲述最后两个主角,deviceclass的创建。讲完这些就会来个大集合。让四大天王(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之类的函数对其进行封装。
#define class_register(class) /

({ /

static struct lock_class_key __key; /

__class_register(class, &__key); /

})

int __class_register(struct class *cls, struct lock_class_key *key)

{

struct subsys_private *cp;

int error;

pr_debug("device class '%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, "struct class mutex", key);

error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);

if (error) {

kfree(cp);

return error;

}

/* set the default /sys/dev directory for devices of this class */

if (!cls->dev_kobj)

cls->dev_kobj = sysfs_dev_char_kobj;

#if defined(CONFIG_BLOCK)

/* let the block class directory show up in the root of sysfs */

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);

return error;

}

error = add_class_attrs(class_get(cls));

class_put(cls);

return error;

}

实在是太简单了,哥都不想分析了。本着对菜鸟同志负责任的态度,我还是讲一下几个关键的变量初始化的地方吧。

if (!cls->dev_kobj)

cls->dev_kobj = sysfs_dev_char_kobj;

#if defined(CONFIG_BLOCK)

/* let the block class directory show up in the root of sysfs */

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.cdriver_init()中调用对应的子函数来初始化及注册。

下面来看看device_register()。如果看懂了bus_register()driver_register()。这个应该不会有太大的问题。先给出函数定义:

int device_register(struct device *dev)

{

device_initialize(dev);

return device_add(dev);

}

driver_register太像了,函数命名恰如其份的反应了他的功能。首先初始化dev。然后再将其加到我们的大家庭中。

void device_initialize(struct device *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);

int device_add(struct device *dev)

{

struct device *parent = NULL;

struct class_interface *class_intf;

int error = -EINVAL;

dev = get_device(dev);

if (!dev)

goto done;

if (!dev->p) {

error = device_private_init(dev);

if (error)

goto done;

}

/*

 * for statically allocated devices, which should all be converted

 * some day, we need to initialize the name. We prevent reading back

 * the name, and force the use of dev_name()

 */

if (dev->init_name) {

dev_set_name(dev, "%s", dev->init_name);

dev->init_name = NULL;

}

if (!dev_name(dev)) {

error = -EINVAL;

goto name_error;

}

pr_debug("device: '%s': %s/n", dev_name(dev), __func__);

parent = get_device(dev->parent);

setup_parent(dev, parent);

/* use parent numa_node */

if (parent)

set_dev_node(dev, dev_to_node(parent));

/* first, register with generic layer. */

/* we require the name to be set before, and pass NULL */

error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);

if (error)

goto Error;

/* notify platform of device entry */

if (platform_notify)

platform_notify(dev);

error = device_create_file(dev, &uevent_attr);

if (error)

goto attrError;

if (MAJOR(dev->devt)) {

error = device_create_file(dev, &devt_attr);

if (error)

goto ueventattrError;

error = device_create_sys_dev_entry(dev);

if (error)

goto devtattrError;

devtmpfs_create_node(dev);

}

error = device_add_class_symlinks(dev);

if (error)

goto SymlinkError;

error = device_add_attrs(dev);

if (error)

goto AttrsError;

error = bus_add_device(dev);

if (error)

goto BusError;

error = dpm_sysfs_add(dev);

if (error)

goto DPMError;

device_pm_add(dev);

/* Notify clients of device addition.  This call must come

 * after dpm_sysf_add() and before kobject_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);

/* tie the class to the device */

klist_add_tail(&dev->knode_class,

       &dev->class->p->klist_devices);

/* notify any interfaces that the device is here */

list_for_each_entry(class_intf,

    &dev->class->p->class_interfaces, node)

if (class_intf->add_dev)

抱歉!评论已关闭.