Linux核心大講堂 (一) 裝置驅動的基石驅動模型(6)

來源:互聯網
上載者:User

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.