可能把驅動模型放在第一章講會會有點難度,但是只要能跨過這道坎,後面就會輕鬆很多,驅動模型是整個linux裝置驅動的基石。大部分人把驅動模型叫做裝置模型,但是我查了linux的協助文檔,就是在下載源碼路徑下的Documentation目錄中找到driver-model這個目錄,裡麵包含的檔案就是我在本章中所要講述的東西,也就是我所說的驅動模型。因此本文都會用驅動模型這個術語(如果各位覺得這種叫法是錯誤的,請在評論中指出,並給出理由,本人非常誠懇的接受各位善意的批評與指正)。驅動模型的核心結構就是我們通常所說的bus、device、device_driver。即匯流排、裝置、裝置驅動。首先分析linux核心要有層次的概念,linux從設計上來說是一層套一層的,那麼在這一層之下,還有一層由kobject、kobj_type、kset這三者組成,也可以認為其屬於驅動模型的範圍內,我們可以看到核心對它的描述是:generic kernel object infrastructure。就是通用核心對象基礎的意思。我們暫且叫它核心對象層吧。在驅動模型的上層我們可以封裝各種子模組子系統,這個以後再做講解。
我們首先來看看核心對象層是什麼東西,都有些什麼功能。在這個分析的過程中請多一點耐心,在這中間需要的僅僅是耐心而已。
首先給出核心對象層各成員的原型:
struct kobject {
const char *name;
struct list_head entry;
struct kobject *parent;
struct kset *kset;
struct kobj_type *ktype;
struct sysfs_dirent *sd;
struct kref kref;
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
struct kset_uevent_ops *uevent_ops;
};
struct kobj_type {
void (*release)(struct kobject *kobj);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};
首先從各結構體的成員我們發現這沒有三角戀的關係,kobject中包含有kobj_type和kset及自身,kset中包含有kobject,而kobj_type則不包含上面兩者,只要是在道上混的兄弟,一眼就可以看出kobject在這場戀愛關係中是佔據絕對地位的。
針對這三者,linux核心提供了一些操作這些成員的方法。(位於kobject.c)。
我們挑幾個名角講一講:
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
char *err_str;
if (!kobj) {
err_str = "invalid kobject pointer!";
goto error;
}
if (!ktype) {
err_str = "must have a ktype to be initialized properly!/n";
goto error;
}
if (kobj->state_initialized) {
/* do not error out as sometimes we can recover */
printk(KERN_ERR "kobject (%p): tried to init an initialized "
"object, something is seriously wrong./n", kobj);
dump_stack();
}
kobject_init_internal(kobj);
kobj->ktype = ktype;
return;
error:
printk(KERN_ERR "kobject (%p): %s/n", kobj, err_str);
dump_stack();
}
我們避重就輕的看一下(前面初始化和合法條件判斷在實際的運行當中是很重要的,但是對我們分析來說只要能抓主線,分析我們感興趣的內容就可以了),可以把這個函數簡化:
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
char *err_str;
kobject_init_internal(kobj);
kobj->ktype = ktype;
return;
}
OK,我們看見傳入了兩個參數,一個是kobject的指標,一個是kobj_type的指標,在調用完kobject_init_internal(kobj)之後,就將傳入的ktype賦值給了kobject的ktype成員。我們先來看看ktype到底是何方神勝。在分析ktype之前,我們先要往上跑一層,這一層我們選擇int device_register(struct device *dev)這個函數,先給出函數原型:
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
INIT_LIST_HEAD(&dev->dma_pools);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev, 0);
device_pm_init(dev);
set_dev_node(dev, -1);
}
我們找到我們感興趣的kobject_init(&dev->kobj, &device_ktype);
我們查看device_ktype的定義:
static struct kobj_type device_ktype = {
.release = device_release,
.sysfs_ops = &dev_sysfs_ops,
};
很明顯release的作用就是release,至於怎麼release我們先不看,下面一個就是sysfs_ops。這個sysfs與使用者空間通訊的一個介面,我們點擊進去查看一下:
static struct sysfs_ops dev_sysfs_ops = {
.show = dev_attr_show,
.store = dev_attr_store,
};分別對應了我們讀和寫sysfs下面節點的兩個動作。至於裡面幹嘛的我們先不管。從上面我們知道,ktype包含了一個sysfs的讀寫介面,另外包含了一個具有release功能的函數。
回到我們之前的內容:簡化版的kobject_init函數:
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
char *err_str;
kobject_init_internal(kobj);
kobj->ktype = ktype;
return;
}
剩下的就是kobject_init_internal(kobj)了。
static void kobject_init_internal(struct kobject *kobj)
{
if (!kobj)
return;
kref_init(&kobj->kref);
INIT_LIST_HEAD(&kobj->entry);
kobj->state_in_sysfs = 0;
kobj->state_add_uevent_sent = 0;
kobj->state_remove_uevent_sent = 0;
kobj->state_initialized = 1;
}
這個函數的功能純粹就是初始化。
從這個初始經我們瞭解一些更新的東西:
kref_init(&kobj->kref);
這個叫引用計數,kref_init的作用就是將kobjct->kref設為1。
接下來就是初始化kobject->entry這條鏈表(linux核心的鏈表是非常重要且比較精妙的,網上相關的好文章也很多,請同志們自行查閱學習)。
接下來就是一堆的位域。
kobj->state_in_sysfs這個成員正如其名:指明是否使用了sysfs。初始化為0,顯然是說:哥現在還沒用。
kobj->state_add_uevent_sent、kobj->state_remove_uevent_sent 這兩個成員的名命也是非常直觀的:指明是否有載入或刪除事件。這個是和熱插拔相關的,當我們增加一個裝置或者刪除一個裝置的時候,會在合適的時候將此位域置為1。
kobj->state_initialized指明kobject是否有被初始化,這們是唯一個置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))
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->class_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))
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;
}
當你看到這一大段的時候,是不是感覺很鬱悶,我也很鬱悶,但是哥很高興的說:依我們目前的功能,我們只分析kobject_add(&dev->kobj, dev->kobj.parent, NULL)就夠了。從人生的低穀瞬間又找回自信其實很簡單,就在現在。先給出函數定義:
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...)
{
va_list args;
int retval;
if (!kobj)
return -EINVAL;
if (!kobj->state_initialized) {
printk(KERN_ERR "kobject '%s' (%p): tried to add an "
"uninitialized object, something is seriously wrong./n",
kobject_name(kobj), kobj);
dump_stack();
return -EINVAL;
}
va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args);
return retval;
}
這下代碼少多了。
我們可以看到核心函數是kobject_add_varg(kobj, parent, fmt, args),其定義如下:
static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
const char *fmt, va_list vargs)
{
int retval;
retval = kobject_set_name_vargs(kobj, fmt, vargs);
if (retval) {
printk(KERN_ERR "kobject: can not set name properly!/n");
return retval;
}
kobj->parent = parent;
return kobject_add_internal(kobj);
}
其中的kobject_set_name_vargs就是用於設定kobject的名字。
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
va_list vargs)
{
const char *old_name = kobj->name;
char *s;
if (kobj->name && !fmt)
return 0;
kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
if (!kobj->name)
return -ENOMEM;
/* ewww... some of these buggers have '/' in the name ... */
while ((s = strchr(kobj->name, '/')))
s[0] = '!';
kfree(old_name);
return 0;
}
下面就是kobject_add_internal這個函數了,其定義如下:
static int kobject_add_internal(struct kobject *kobj)
{
int error = 0;
struct kobject *parent;
if (!kobj)
return -ENOENT;
if (!kobj->name || !kobj->name[0]) {
WARN(1, "kobject: (%p): attempted to be registered with empty "
"name!/n", kobj);
return -EINVAL;
}
parent = kobject_get(kobj->parent);
/* join kset if set, use it as parent if we do not already have one */
if (kobj->kset) {
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj);
kobj->parent = parent;
}
pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'/n",
kobject_name(kobj), kobj, __func__,
parent ? kobject_name(parent) : "<NULL>",
kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
error = create_dir(kobj);
if (error) {
kobj_kset_leave(kobj);
kobject_put(parent);
kobj->parent = NULL;
/* be noisy on error issues */
if (error == -EEXIST)
printk(KERN_ERR "%s failed for %s with "
"-EEXIST, don't try to register things with "
"the same name in the same directory./n",
__func__, kobject_name(kobj));
else
printk(KERN_ERR "%s failed for %s (%d)/n",
__func__, kobject_name(kobj), error);
dump_stack();
} else
kobj->state_in_sysfs = 1;
return error;
}
憑著一個程式員的直覺,我們可以看到最重要的是create_dir(kobj);沒錯,哥猜的很對,就是它了,它和sysfs相關,建立了一個目錄,具體這個函數因為牽涉的非常廣,我們暫且不做分析。君子報仇,十年不晚,我們看誰笑到最後。在create_dir(kobj)之後我們將kobj->state_in_sysfs =置為1,很親切吧。撞到老相識的感覺很爽吧,我們在後續分析核心的過程中會撞到越來越多的老相識,並且結識更多的新朋友。連著名歌唱家殷秀梅都知道學習核心的方法:結識新朋友,不忘老朋友……(80後朋友應該都認識,90後的有可能就不認識了)。
接下來我們來分析一下和kset有關的一個函數,那就是先給出函數原型:
struct kset *kset_create_and_add(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int error;
kset = kset_create(name, uevent_ops, parent_kobj);
if (!kset)
return NULL;
error = kset_register(kset);
if (error) {
kfree(kset);
return NULL;
}
return kset;
}
和上一節分析kobject一樣,為了更好的講解這個函數我們先要跳到上一層,我們先有必要看一下都有哪些朋友調用了它:int bus_register(struct bus_type *bus)。大名鼎鼎的匯流排註冊。
我們看到bus_register函數中有這樣幾行代碼:
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
可見kset和匯流排是關係的。OK。我們以第一段為基礎講解,分別傳入了一個常字串”devices”,一個null 指標,一個kobject指標。
函數內部首先調用
static struct kset *kset_create(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int retval;
kset = kzalloc(sizeof(*kset), GFP_KERNEL); //分配一個kset結構體並初始化
if (!kset)
return NULL;
retval = kobject_set_name(&kset->kobj, name); //將傳入的常字串賦值給//kset->kobj->name
if (retval) {
kfree(kset);
return NULL;
}
kset->uevent_ops = uevent_ops; //將uevent_ops
kset->kobj.parent = parent_kobj; //將父類kobject指標賦值給kset->kobj.parent
/*
* The kobject of this kset will have a type of kset_ktype and belong to
* no kset itself. That way we can properly free it when it is
* finished being used.
*/
kset->kobj.ktype = &kset_ktype; //將kset_ktyp賦值給kset->kobj.parent
kset->kobj.kset = NULL; //將NULL賦值給kset->kobj.kset
return kset;
}
從上面標紅的注釋我們發現kset內嵌的kobject的重要性了。這是kset和kobject的重要關係。有一句話來形容叫我中有你,你中有我。接下來我們將做好的kset的指標的形式傳給kset_register.
int kset_register(struct kset *k)
{
int err;
if (!k)
return -EINVAL;
kset_init(k);
err = kobject_add_internal(&k->kobj);
if (err)
return err;
kobject_uevent(&k->kobj, KOBJ_ADD);
return 0;
}
void kset_init(struct kset *k)
{
kobject_init_internal(&k->kobj);
INIT_LIST_HEAD(&k->list);
spin_lock_init(&k->list_lock);
}
接下來就是kobject_add_internal(&k->kobj),又撞到老相識了,讓我們再次高歌:結識新朋友,不忘老朋友…
好了,kobject,kobj_type,kset的關係我們大概清楚了,下面是我畫的一個圖用於表示這三者的關係:
好了,先抽根煙吧,下節我們繼續分析。