国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
Linux驅(qū)動(dòng)程序開(kāi)發(fā) - 設(shè)備驅(qū)動(dòng)模型初探
序言
從這一章開(kāi)始,我們將詳細(xì)的介紹Linux的設(shè)備驅(qū)動(dòng)模型。Linux設(shè)備驅(qū)動(dòng)模型是一個(gè)相當(dāng)復(fù)雜的系統(tǒng),對(duì)于初學(xué)者來(lái)說(shuō)真有些無(wú)從入手。而且更加困難的是,隨著新的LinuxKernel的release,Linux的設(shè)備驅(qū)動(dòng)模型總會(huì)有或大或小的變化,我們將盡量展現(xiàn) Linux Kernel的這種變化。
早期的Linux內(nèi)核(版本2.4之前)并沒(méi)有實(shí)現(xiàn)一個(gè)統(tǒng)一的設(shè)備模型,設(shè)備節(jié)點(diǎn)的創(chuàng)建一般是mknod命令手動(dòng)創(chuàng)建或利用devfs文件系統(tǒng)創(chuàng)建。早期的Linux發(fā)行版一般會(huì)采用手動(dòng)創(chuàng)建的方式預(yù)先把通常用到的節(jié)點(diǎn)都創(chuàng)建出來(lái),而嵌入式系統(tǒng)則會(huì)采用devfs的方式。起初Linux2.6內(nèi)核還支持devfs,但從2.6.18開(kāi)始,內(nèi)核完全移除了devfs系統(tǒng)而采用的udev的方式動(dòng)態(tài)的創(chuàng)建設(shè)備節(jié)點(diǎn)。因此,新的Linux發(fā)行版都采用udev的方式管理設(shè)備節(jié)點(diǎn)文件。(關(guān)于udev的詳細(xì)信息,請(qǐng)參考:http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html)。
Linux2.6設(shè)備驅(qū)動(dòng)模型的基本元素是Class、Bus、Device、Driver,下面我們分別介紹各個(gè)部分。
Class 和ClassDevice
驅(qū)動(dòng)模型最基本的概念是設(shè)備及其類別,Linux中使用struct class 和structclass_device來(lái)管理不同類別的設(shè)備。由于設(shè)備驅(qū)動(dòng)模型是一個(gè)復(fù)雜的系統(tǒng),我們還是從一個(gè)簡(jiǎn)單的例子開(kāi)始介紹,然后在逐步展開(kāi)。其實(shí)實(shí)現(xiàn)設(shè)備節(jié)點(diǎn)的動(dòng)態(tài)創(chuàng)建是一個(gè)很簡(jiǎn)單的事情,并不需要太多的代碼。我們修改我們的驅(qū)動(dòng)初始化函數(shù)如下:
#include <linux/device.h>
#define DEVNAME "hello"
static dev_t dev;
static struct class *hello_class;
static struct cdev *hello_cdev;
static int __init hello_init(void)
{
int error;
error = alloc_chrdev_region(&dev, 0, 2, "hello");
if (error)
{
printk("hello: alloc_chardev_region failed!\n");
goto out;
}
hello_cdev = cdev_alloc();
if (hello_cdev == NULL)
{
printk("hello: alloc cdev failed!\n");
error = -ENOMEM;
goto out_chrdev;
}
hello_cdev->ops = &hello_fops;
hello_cdev->owner = THIS_MODULE;
error = cdev_add(hello_cdev, dev, 1);
if (error)
{
printk("hello: cdev_add failed!\n");
goto out_cdev;
}
hello_class = class_create(THIS_MODULE, DEVNAME);
if (IS_ERR(hello_class))
{
error = PTR_ERR(hello_class);
goto out_chrdev;
}
class_device_create(hello_class, NULL, dev, NULL, DEVNAME);
memset (hello_buf, 0, sizeof(hello_buf));
memcpy(hello_buf, DEFAULT_MSG, sizeof(DEFAULT_MSG));
printk("hello: Hello World!\n");
return 0;
out_cdev:
cdev_del(hello_cdev);
out_chrdev:
unregister_chrdev_region(hello_cdev->dev, 2);
out:
return error;
}
static void __exit hello_exit(void)
{
class_device_destroy(hello_class, dev);
class_destroy(hello_class);
unregister_chrdev_region(hello_cdev->dev, 2);
cdev_del(hello_cdev);
printk("hello: Goodbye World\n");
}
重新編譯這個(gè)驅(qū)動(dòng)程序,當(dāng)加載這個(gè)驅(qū)動(dòng)到內(nèi)核中時(shí),系統(tǒng)(一般是hotplug和udev系統(tǒng))就會(huì)自動(dòng)的創(chuàng)建我們指定的設(shè)備名字:/dev/hello,同時(shí),你也可以發(fā)現(xiàn)在sysfs系統(tǒng)中添加了新的文件:/sys/class/hello/hello/。
當(dāng)然并不需要把所有的代碼都貼到這里,但是這樣做可能更加清楚。我們把添加的代碼顯示成藍(lán)色,這樣你可以清楚的看到代碼的簡(jiǎn)單程度。這里主要用到了class_create和class_device_create函數(shù),它們定義在<linux/device.h>頭文件中。
extern struct class *class_create(struct module *owner, const char *name);
extern void class_destroy(struct class *cls);
extern struct class_device *class_device_create(struct class *cls,
struct class_device *parent,
dev_t devt,
struct device *device,
const char *fmt, ...)
__attribute__((format(printf,5,6)));
extern void class_device_destroy(struct class *cls, dev_t devt);
Linux是通過(guò)設(shè)備與設(shè)備類來(lái)管理設(shè)備的,當(dāng)你調(diào)用這些函數(shù)向系統(tǒng)注冊(cè)設(shè)備及其類的時(shí)候,內(nèi)核會(huì)自動(dòng)的在sysfs文件系統(tǒng)中創(chuàng)建對(duì)應(yīng)的文件。如果想了解更多的關(guān)于設(shè)備及其類的函數(shù)接口,請(qǐng)你閱讀Linuxkernel的源文件(include/device.h頭文件和drivers/base/class.c實(shí)現(xiàn)文件)。這里簡(jiǎn)單說(shuō)明class_device_create函數(shù),它的最后一個(gè)參數(shù)是一個(gè)變參,類似于printf函數(shù)參數(shù),用于指定添加設(shè)備的名稱也就是顯示在/dev/目錄下設(shè)備文件名稱。
創(chuàng)建的class_device設(shè)備會(huì)自動(dòng)注冊(cè)到系統(tǒng)中,這樣對(duì)于給定的設(shè)備,系統(tǒng)會(huì)自動(dòng)找到匹配的設(shè)備驅(qū)動(dòng)。
你可以在設(shè)備類或設(shè)備目錄下(/sys/class)創(chuàng)建文件,這個(gè)文件提供了內(nèi)核同用戶空間程序的交互接口。內(nèi)核提供了設(shè)備、設(shè)備類的內(nèi)核函數(shù)接口,這些接口也定義在<include/device.h>頭文件中。
int class_create_file(struct class *cls, const struct class_attribute *attr);
void class_remove_file(struct class *cls, const struct class_attribute *attr);
int class_device_create_file(struct class_device *class_dev,
const struct class_device_attribute *attr);
void class_device_remove_file(struct class_device *class_dev,
const struct class_device_attribute *attr);
int class_device_create_bin_file(struct class_device *class_dev,
struct bin_attribute *attr);
void class_device_remove_bin_file(struct class_device *class_dev,
struct bin_attribute *attr);
其實(shí),這些函數(shù)僅僅是簡(jiǎn)單的封裝了sysfs文件系統(tǒng)函數(shù),但它為設(shè)備及設(shè)備類提供了統(tǒng)一的函數(shù)接口。
Drivers
當(dāng)一個(gè)設(shè)備注冊(cè)到系統(tǒng)中時(shí),它就向系統(tǒng)表明了哪個(gè)驅(qū)動(dòng)匹配這個(gè)設(shè)備。Linux內(nèi)核會(huì)在注冊(cè)的設(shè)備驅(qū)動(dòng)中查找匹配的驅(qū)動(dòng)并調(diào)用對(duì)應(yīng)的探測(cè)函數(shù)(probe)來(lái)初始化設(shè)備。設(shè)備驅(qū)動(dòng)接口也定義在<linux/device.h>頭文件中。
struct device_driver {
const char        *name;
struct bus_type        *bus;
struct module        *owner;
const char         *mod_name;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
struct attribute_group **groups;
struct driver_private *p;
};
其中,比較重要的數(shù)據(jù)是name,Linux內(nèi)核就是根據(jù)name來(lái)匹配設(shè)備與驅(qū)動(dòng)的。probe函數(shù)用于設(shè)備的探測(cè)及初始化,remove函數(shù)在設(shè)備移出系統(tǒng)時(shí)被觸發(fā)調(diào)用。
一般來(lái)說(shuō),我們的驅(qū)動(dòng)需要實(shí)現(xiàn)name、module、probe、remove函數(shù)。
Bus
通常我們的驅(qū)動(dòng)并不需要實(shí)現(xiàn)Bus接口,也沒(méi)有這個(gè)必要,因此你完全可以跳過(guò)這段,除非你想添加一個(gè)新的總線到系統(tǒng)中。
在Linux2.6內(nèi)核中,structbus_type描述了一個(gè)bus對(duì)象,它定義在<linux/device.h>頭文件中。(你發(fā)現(xiàn)沒(méi)有,到目前Linux設(shè)備模型接口都定義在這個(gè)文件中)
struct bus_type {
const char        *name;
struct bus_attribute    *bus_attrs;
struct device_attribute    *dev_attrs;
struct driver_attribute    *drv_attrs;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*suspend_late)(struct device *dev, pm_message_t state);
int (*resume_early)(struct device *dev);
int (*resume)(struct device *dev);
struct bus_type_private *p;
};
extern int __must_check bus_register(struct bus_type *bus);
extern void bus_unregister(struct bus_type *bus);
extern int __must_check bus_create_file(struct bus_type *,
struct bus_attribute *);
extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
在這個(gè)結(jié)構(gòu)中,name描述了bus的名字,如它會(huì)顯示在/sys/bus/中。match函數(shù)用于匹配屬于這個(gè)bus的設(shè)備和驅(qū)動(dòng),uevent用于處理Linuxuevent事件。probe和remove類似與driver的函數(shù)接口,主要用于設(shè)備的Hotplug處理。其他的函數(shù)是Power相關(guān)的函數(shù)接口。
同樣,bus也需要注冊(cè)到系統(tǒng)中,并可以在sysfs中創(chuàng)建文件。
sysfs文件系統(tǒng)
sysfs類似于proc文件系統(tǒng),用于用戶空間程序和內(nèi)核空間交互數(shù)據(jù)的接口。但sysfs提供了更多的功能,其中之一就是顯示Linux驅(qū)動(dòng)程序模型的分層結(jié)構(gòu)關(guān)系。Ubuntu804的sysfs文件系統(tǒng)的目錄顯示如下:
block  bus class  devices firmware  fs kernel  module power  slab
當(dāng)你瀏覽這個(gè)文件系統(tǒng)的時(shí)候,你會(huì)發(fā)現(xiàn)里面有很多鏈接文件,其實(shí)正是這些鏈接文件展現(xiàn)了Linux驅(qū)動(dòng)模型各個(gè)組成部分之間的關(guān)系。
sysfs文件系統(tǒng)中,最重要的就是structattribute結(jié)構(gòu),它被用來(lái)管理內(nèi)核sysfs文件的接口(名字,屬性,讀寫(xiě)函數(shù)等)。內(nèi)核sysfs提供了基本的attribute接口,不同的設(shè)備如bus、device在基本attribute的基礎(chǔ)上定義了自己的讀寫(xiě)函數(shù),sysfs提供了對(duì)應(yīng)的宏來(lái)簡(jiǎn)化屬性的操作。請(qǐng)參考<linux/sysfs.h>頭文件中。
struct attribute {
const char        *name;
struct module     *owner;
mode_t            mode;
};
#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode },    \
.show    = _show,                    \
.store    = _store,                    \
}
int __must_check sysfs_create_file(struct kobject *kobj, const struct attribute *attr);
int __must_check sysfs_create_dir(struct kobject *kobj);
我們看到,sysfs的structattribute結(jié)構(gòu)本身并不包含讀寫(xiě)訪問(wèn)函數(shù),驅(qū)動(dòng)模型的各個(gè)部分都會(huì)擴(kuò)展這個(gè)結(jié)構(gòu)并定義自己的屬性結(jié)構(gòu)來(lái)引入各自的操作函數(shù),如class:(這個(gè)結(jié)構(gòu)定義在<linux/device.h>頭文件中)。
struct class_attribute {
struct attribute    attr;
ssize_t (*show)(struct class *, char * buf);
ssize_t (*store)(struct class *, const char * buf, size_t count);
};
#define CLASS_ATTR(_name, _mode, _show, _store)            \
struct class_attribute class_attr_##_name = __ATTR(_name, _mode, _show, _store)
關(guān)于sysfs的更多信息,請(qǐng)參考Linux內(nèi)核源代碼樹(shù)中的Documentation/filesystems/sysfs.txt文件。
Platform總線
platform總線是Linux內(nèi)核中的一個(gè)虛擬總線,它使得設(shè)備的管理更加簡(jiǎn)單化。目前大部分的驅(qū)動(dòng)都是用platform總線來(lái)寫(xiě)的。platform總線模型的各個(gè)部分都是繼承自Device模型(姑且這么說(shuō)吧),它在系統(tǒng)內(nèi)實(shí)現(xiàn)了個(gè)虛擬的總線,即platform_bus,如果你的設(shè)備需要platform總線管理,那么就需要向系統(tǒng)中注冊(cè)platform設(shè)備及其驅(qū)動(dòng)程序。就像前面所介紹的那樣,platform總線分為platform_bus,platform_device和platform_driver幾個(gè)部分,他們的接口定義在<linux/platform.h>頭文件中。
platform bus
我們先來(lái)看看platform_bus的定義:
struct device platform_bus = {
.bus_id        = "platform",
};
struct bus_type platform_bus_type = {
.name        = "platform",
.dev_attrs    = platform_dev_attrs,
.match        = platform_match,
.uevent        = platform_uevent,
.suspend    = platform_suspend,
.suspend_late    = platform_suspend_late,
.resume_early    = platform_resume_early,
.resume        = platform_resume,
};
int __init platform_bus_init(void)
{
int error;
error = device_register(&platform_bus);
if (error)
return error;
error =  bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
return error;
}
platform_bus數(shù)據(jù)結(jié)構(gòu)描述了platform bus設(shè)備,platform_bus_type描述了platformbus總線,它提供了platform總線設(shè)備和驅(qū)動(dòng)的匹配函數(shù)。platform總線是由函數(shù)platform_bus_init(void)初始化的。
對(duì)于Linux我們一般的設(shè)備驅(qū)動(dòng)程序來(lái)說(shuō),就像前面Bus一段提到的那樣,我們不需要關(guān)心platform總線本身,我們只要調(diào)用我們的設(shè)備和驅(qū)動(dòng)接口就可以了。
Platform Device
如果你想讓platform總線來(lái)管理設(shè)備,那么,你需要先向platform系統(tǒng)注冊(cè)設(shè)備,這個(gè)過(guò)程是通過(guò)下面的函數(shù)接口來(lái)實(shí)現(xiàn)的:
int platform_device_add(struct platform_device *pdev);
int platform_device_register(struct platform_device *pdev);
我們一般需要調(diào)用platform_device_register函數(shù)來(lái)向系統(tǒng)添加platform設(shè)備。這兩個(gè)函數(shù)唯一的差別就是platform_device_register在添加設(shè)備前會(huì)初始化platform_device的dev數(shù)據(jù)成員,它是一個(gè)structdevice類型數(shù)據(jù)。當(dāng)一個(gè)platform_device添加到platform總線中后,platform總線就會(huì)為它找到匹配的設(shè)備驅(qū)動(dòng)程序,很顯然,在這之前,你需要向系統(tǒng)注冊(cè)platform_driver。
Platform Driver
我們先來(lái)看看platform總線設(shè)備驅(qū)動(dòng)的結(jié)構(gòu):
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
extern int platform_driver_register(struct platform_driver *);
很顯然,它“繼承”自structdevice_driver,同樣類似于structdevice_driver,一般我們需要實(shí)現(xiàn)probe函數(shù),及指定platform_driver能驅(qū)動(dòng)的設(shè)備的名字。
使用Platform總線
下面這個(gè)例子告訴你如何使用platoform總線,這是一個(gè)AndroidGoldfishGPIO驅(qū)動(dòng)程序。它本身就是一個(gè)platform設(shè)備驅(qū)動(dòng)(goldfish-gpio),同時(shí),它又會(huì)向系統(tǒng)注新的設(shè)備(android-timed-gpio),這個(gè)新設(shè)備又被timed_output.c驅(qū)動(dòng)程序驅(qū)動(dòng)。
......
#include <linux/platform_device.h>
struct platform_device timed_gpio_device = {
.name  = "android-timed-gpio",
.id    = -1,
.dev.platform_data = &timed_gpio_platform_data,
};
static int goldfish_gpio_probe(struct platform_device *pdev)
{
struct goldfish_gpio_data *gpio_data;
......
error = platform_device_register(&timed_gpio_device);
......
return 0;
}
static int goldfish_gpio_remove(struct platform_device *pdev)
{
int i;
struct goldfish_gpio_data *gpio_data;
......
platform_device_unregister(&timed_gpio_device);
......
return 0;
}
static struct platform_driver goldfish_gpio_driver = {
.probe    = goldfish_gpio_probe,
.remove   = goldfish_gpio_remove,
.driver = {
.name = "goldfish-gpio"
}
};
static int __init goldfish_gpio_init(void)
{
return platform_driver_register(&goldfish_gpio_driver);
}
static void __exit goldfish_gpio_exit(void)
{
platform_driver_unregister(&goldfish_gpio_driver);
}
這個(gè)新注冊(cè)的設(shè)備(timed_gpio_device)由timed_output驅(qū)動(dòng)管理,通過(guò)瀏覽這段代碼,你應(yīng)該對(duì)如何使用platform總線有個(gè)全面的了解。(本想把全部code放在這里,但超過(guò)最大字?jǐn)?shù)限制!)
static struct class *timed_gpio_class;
struct timed_gpio_data {
struct device *dev;
struct hrtimer timer;
spinlock_t lock;
unsigned     gpio;
int         max_timeout;
u8         active_low;
};
......
static int android_timed_gpio_probe(struct platform_device *pdev)
{
struct timed_gpio_platform_data *pdata = pdev->dev.platform_data;
struct timed_gpio *cur_gpio;
struct timed_gpio_data *gpio_data, *gpio_dat;
int i, ret = 0;
......
}
static int android_timed_gpio_remove(struct platform_device *pdev)
{
}
static struct platform_driver android_timed_gpio_driver = {
.probe        = android_timed_gpio_probe,
.remove        = android_timed_gpio_remove,
.driver        = {
.name        = "android-timed-gpio",
.owner        = THIS_MODULE,
},
};
static int __init android_timed_gpio_init(void)
{
timed_gpio_class = class_create(THIS_MODULE, "timed_output");
if (IS_ERR(timed_gpio_class))
return PTR_ERR(timed_gpio_class);
return platform_driver_register(&android_timed_gpio_driver);
}
static void __exit android_timed_gpio_exit(void)
{
class_destroy(timed_gpio_class);
platform_driver_unregister(&android_timed_gpio_driver);
}
Kobject和kset
提到Linux的設(shè)備模型,就不得不提kobject和kset這兩個(gè)內(nèi)核對(duì)象,他們才是Linux內(nèi)核設(shè)備模型的最基礎(chǔ)的結(jié)構(gòu),但講解他們卻是一個(gè)枯燥過(guò)程,限于篇幅,這個(gè)就不作介紹了,請(qǐng)參考Linux文檔<documentation/kobject.txt>。
后記
在這里,我們簡(jiǎn)單的介紹了Linux的設(shè)備模型,包括基本總線、設(shè)備、驅(qū)動(dòng)的關(guān)系,同時(shí)也簡(jiǎn)單的介紹了Linux2.6內(nèi)核的platform總線。這些內(nèi)容應(yīng)該足夠讓你了解如何使用Linux設(shè)備模型來(lái)管理設(shè)備了。
原文
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開(kāi)APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
linux中probe函數(shù)傳遞參數(shù)的尋找(下)
Linux下spi驅(qū)動(dòng)開(kāi)發(fā)(1)
字符設(shè)備驅(qū)動(dòng)之LED-平臺(tái)設(shè)備驅(qū)動(dòng)(platform設(shè)備驅(qū)動(dòng))
platform驅(qū)動(dòng)編寫(xiě) 基于gpio-keys
Linux設(shè)備模型(8)_platform設(shè)備
Linux驅(qū)動(dòng)的platform機(jī)制
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服