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

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/z2007b/archive/2011/05/19/6432997.aspx

//*****************************************************

 

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之类的函数对其进行封装。
#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.c的driver_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)

class_intf->add_dev(dev, class_intf);

mutex_unlock(&dev->class->p->class_mutex);

}

done:

put_device(dev);

return error;

 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;

goto done;

}

函数本身实在找不到有什么需要讲的。

不过有一点比较重要,就是在设备注册的时候,一个设备不能直接从属于一个类和一条总线。太绕了,实例:

struct device dev;

dev.class = xx;

dev.bus=yy;

device_register(&dev);

这样是不行的,这样会在创建subsystem链接的时候出现重复创建的现象,导致注册失败。其实我们可以这样解决。创建一个父类设备从属于某个class,然后将子类设备的parent指向父类设备地址,再将子类设备的bus指向我的总线的地址。这样就可以解决了。下一节就是四大天王齐登台亮相。^_^

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值