本文來自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指向我的匯流排的地址。這樣就可以解決了。下一節就是四大天王齊登台亮相。^_^