Linux驅(qū)動(dòng)(七)設(shè)備模型介紹以及platform設(shè)備驅(qū)動(dòng)

姓名:謝煥彬 學(xué)號(hào):19020100303
前面講過(guò)了字符驅(qū)動(dòng)砾医,我們把過(guò)程再來(lái)回顧一下拿撩,我們是如何來(lái)完成一個(gè)驅(qū)動(dòng)的。

1如蚜、設(shè)備號(hào)相關(guān)問(wèn)題压恒,手動(dòng)或自動(dòng)創(chuàng)建設(shè)備號(hào)。

2错邦、設(shè)備對(duì)象相關(guān)問(wèn)題探赫,完成驅(qū)動(dòng)操作方法集合,并向內(nèi)核注冊(cè)該設(shè)備對(duì)象撬呢。

3伦吠、生成設(shè)備節(jié)點(diǎn)

這其中有一個(gè)最大的問(wèn)題:設(shè)備和驅(qū)動(dòng)高度耦合,設(shè)備修改后魂拦,驅(qū)動(dòng)也需要修改毛仪,牽一發(fā)而動(dòng)全身。這為后續(xù)的驅(qū)動(dòng)開(kāi)發(fā)造成了很不好的影響芯勘。我們應(yīng)該做到的是箱靴,高內(nèi)聚低耦合。設(shè)備修改后只修改相應(yīng)的設(shè)備文件荷愕,驅(qū)動(dòng)需要修改就只修改驅(qū)動(dòng)部分衡怀。那驅(qū)動(dòng)和設(shè)備如何聯(lián)系起來(lái)的呢棍矛?這就又需要要給文件來(lái)配合,總線狈癞。這就是Linux2.6以后的設(shè)備驅(qū)動(dòng)模型:總線茄靠、設(shè)備茂契、驅(qū)動(dòng)蝶桶。

  那三者是如何分工的呢?

  設(shè)備:描述一個(gè)設(shè)備的詳細(xì)信息掉冶。例如一個(gè)串口真竖,那應(yīng)該體現(xiàn)該串口設(shè)備的相關(guān)信息,例如串口的名稱厌小,設(shè)備的地址恢共,設(shè)備的中斷號(hào)等等。

  驅(qū)動(dòng):描述如何使用設(shè)備的方法璧亚,這個(gè)好說(shuō)讨韭,其實(shí)還是一堆方法嘛。為用戶提供操作設(shè)備的方法癣蟋。

  總線:總得有一個(gè)東西把設(shè)備和驅(qū)動(dòng)聯(lián)系起來(lái)把透硝,那就是總線》杞粒總線中有一個(gè)匹配函數(shù)濒生,系統(tǒng)每注冊(cè)一個(gè)設(shè)備,就去找一下系統(tǒng)中與之匹配的驅(qū)動(dòng)幔欧。同樣罪治,注冊(cè)驅(qū)動(dòng)的時(shí)候,也要去系統(tǒng)中尋找一個(gè)與之匹配的設(shè)備礁蔗。這樣觉义,設(shè)備和驅(qū)動(dòng)就聯(lián)系到一起了。

  系統(tǒng)中有很多總線浴井,I2C總線谁撼,SPI總線,USB總線滋饲。但有很多設(shè)備沒(méi)有總線實(shí)體厉碟,例如LED燈,這算什么總線屠缭?Linux添加了一種虛擬總線箍鼓,叫做平臺(tái)總線---platform。

我們?cè)賮?lái)了解下一個(gè)問(wèn)題呵曹,linux設(shè)備模型的對(duì)象化款咖。c語(yǔ)言是面向過(guò)程的語(yǔ)言何暮,很多高級(jí)語(yǔ)言c++,java等都是面向?qū)ο蟮倪^(guò)程铐殃。c語(yǔ)言同樣可以實(shí)現(xiàn)對(duì)象化的過(guò)程海洼。Linux的設(shè)備模型就向我們很好的展示了這個(gè)過(guò)程。我們就以platform設(shè)備模型為例來(lái)看一看富腊。

   在c語(yǔ)言如何實(shí)現(xiàn)一個(gè)類呢坏逢?用的就是結(jié)構(gòu)體。與類相比赘被,結(jié)構(gòu)體不能定義方法是整,但我們可以用函數(shù)指針來(lái)作為方法。文件操作集合struct file_operations就是一個(gè)典型的例子民假。繼承如何實(shí)現(xiàn)呢浮入?很簡(jiǎn)單,講父類(父結(jié)構(gòu)體)包含就ok了羊异,父類(父結(jié)構(gòu)體)中的變量或者方法就能用了事秀。

   linux設(shè)備驅(qū)動(dòng)模型中的根類是kobject,這是linux中所有驅(qū)動(dòng)模型的基類野舶,我們剛才講的總線易迹、設(shè)備、驅(qū)動(dòng)都是繼承

struct device { //設(shè)備的基類
struct device *parent;

struct device_private   *p;

struct kobject kobj;         //++++++++++++++++++++++繼承自kobject
const char      *init_name; /* initial name of the device */
const struct device_type *type;

struct mutex        mutex;  /* mutex to synchronize calls to
                 * its driver.
                 */

struct bus_type *bus;       /* type of bus device is on */
struct device_driver *driver;   /* which driver has allocated this
                   device */
void        *platform_data; /* Platform specific data, device
                   core doesn't touch it */
struct dev_pm_info  power;
struct dev_power_domain *pwr_domain;

...........}
struct device_driver {//設(shè)備驅(qū)動(dòng)基類結(jié)構(gòu)體
const char *name;
struct bus_type *bus;

struct module       *owner;
const char      *mod_name;  /* used for built-in modules */

bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */

const struct of_device_id   *of_match_table;

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);
const struct attribute_group **groups;

const struct dev_pm_ops *pm;

struct driver_private *p;//這個(gè)里面包含object

};
struct driver_private {
struct kobject kobj; //父類
struct klist klist_devices;
struct klist_node knode_bus;
struct module_kobject *mkobj;
struct device_driver *driver;
};
總線基類結(jié)構(gòu)體
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 (*resume)(struct device *dev);

const struct dev_pm_ops *pm;

struct subsys_private *p;//這個(gè)里面繼續(xù)++++++++++++++=

};
struct subsys_private {
struct kset subsys; //kset是object基類的集合
struct kset *devices_kset;

struct kset *drivers_kset;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;

struct list_head class_interfaces;
struct kset glue_dirs;
struct mutex class_mutex;
struct class *class;

};
int (*match)(struct device *dev, struct device_driver *drv)筒愚;總線中的match方法就是用來(lái)匹配驅(qū)動(dòng)和設(shè)備的赴蝇。
struct bus_type、struct device 巢掺、struct device_driver 分別是總線句伶、設(shè)備、驅(qū)動(dòng)的基類結(jié)構(gòu)體陆淀。具體的總線考余、設(shè)備、驅(qū)動(dòng)都繼承自這里轧苫。
繼續(xù)楚堤,來(lái)看看我們的平臺(tái)設(shè)備platform相關(guān)的類。

1含懊、platform總線

platform總線是bus_type的一個(gè)對(duì)象實(shí)例身冬。

struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};

借此我們來(lái)看一下,platform是如何匹配的驅(qū)動(dòng)和設(shè)備的呢岔乔?

static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);

/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
    return 1;

/* Then try to match against the id table */
if (pdrv->id_table)
    return platform_match_id(pdrv->id_table, pdev) != NULL;

/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);

}
匹配方法有三種:1酥筝、基于設(shè)備樹(shù)風(fēng)格的匹配。2雏门、基于id_table表的匹配 3嘿歌、基于設(shè)備name和驅(qū)動(dòng)name的匹配掸掏。設(shè)備樹(shù)的方式我們先不講,我們介紹一下后后兩種方式宙帝。請(qǐng)繼續(xù)向下看丧凤。
2、platform設(shè)備

struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;

const struct platform_device_id *id_entry;

/* MFD cell pointer */
struct mfd_cell *mfd_cell;

/* arch specific additions */
struct pdev_archdata    archdata;

};
name:設(shè)備名稱步脓,可以用來(lái)與驅(qū)動(dòng)名稱進(jìn)行匹配愿待,match函數(shù)中的name匹配方法就是比對(duì)這里的name
id: 設(shè)備id,現(xiàn)在我們一般直接設(shè)置成-1沪编,不用它了呼盆。

dev: 設(shè)備父類

num_resources:資源數(shù)量

resource:資源結(jié)構(gòu)體年扩。這是設(shè)備最重要的東西蚁廓。

id_entry:也是用來(lái)匹配,match函數(shù)中id_table匹配的方法厨幻。

resource是設(shè)備中最重要的東西相嵌。驅(qū)動(dòng)要操作設(shè)備,首先需要知道什么呢况脆?當(dāng)然是設(shè)備地址了饭宾,如果有中斷還需要知道中斷號(hào),這就是resource的作用格了。

struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
start:資源起始(起始地址或中斷號(hào)起始值)
end:資源結(jié)束(結(jié)束地址或中斷號(hào)結(jié)束值)

name:名稱

flags:資源標(biāo)志定義看铆,表征是什么類型的資源,最常用的有以下幾個(gè):

      IORESOURCE_MEM:內(nèi)存資源盛末,也包括IO內(nèi)存弹惦,

     IORESOURCE_IRQ:中斷資源

      IORESOURCE_DMA :DMA通道資源

3、platform驅(qū)動(dòng)

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 (
resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
platform驅(qū)動(dòng)中包含多個(gè)方法悄但、一個(gè)設(shè)備基類棠隐、一個(gè)id_table表
probe:這是匹配上之后要執(zhí)行的函數(shù)。

remove:所驅(qū)動(dòng)的平臺(tái)設(shè)備被移除時(shí)或平臺(tái)驅(qū)動(dòng)注銷時(shí)所用

driver:基類檐嚣,driver中的name成員可用來(lái)匹配設(shè)備助泽,match匹配函數(shù)的第三種方法

id_table:可以驅(qū)動(dòng)的平臺(tái)設(shè)備ID列表。match匹配函數(shù)的第二種方法嚎京。主要設(shè)置name即可嗡贺。
struct platform_device_id {
char name[PLATFORM_NAME_SIZE];
kernel_ulong_t driver_data
attribute((aligned(sizeof(kernel_ulong_t))));
};
好,相關(guān)的類介紹完畢鞍帝,現(xiàn)在來(lái)看看相關(guān)的API函數(shù)诫睬。總線相關(guān)的工作一般情況下內(nèi)核已經(jīng)給我們做好了膜眠,我們只需要做設(shè)備和驅(qū)動(dòng)相關(guān)的工作岩臣。
1溜嗜、設(shè)備注冊(cè)與注銷

int platform_add_devices(struct platform_device **devs, int num)
int platform_device_register(struct platform_device *pdev)
第一個(gè)函數(shù)可以注冊(cè)多個(gè)平臺(tái)設(shè)備,第二個(gè)函數(shù)只注冊(cè)一個(gè)設(shè)備架谎,而實(shí)際上platform_add_devices內(nèi)部時(shí)多次調(diào)用platform_device_register函數(shù)的炸宵。
devs是platform_device結(jié)構(gòu)體二級(jí)指針

num是要注冊(cè)的個(gè)數(shù)

pdev是platform_device結(jié)構(gòu)體指針

void platform_device_unregister(struct platform_device *pdev)
設(shè)備注銷函數(shù)。
2谷扣、驅(qū)動(dòng)的注冊(cè)與注銷

int platform_driver_register(struct platform_driver *drv)
驅(qū)動(dòng)的注冊(cè)

void platform_driver_unregister(struct platform_driver *drv)
驅(qū)動(dòng)的注銷
3土全、設(shè)備資源獲取

我們剛才說(shuō)到,設(shè)備中類中有一個(gè)成員變量是resource会涎,是代表設(shè)備的硬件信息裹匙。驅(qū)動(dòng)要操作設(shè)備就必須獲取硬件信息,linux為我們提供了這樣的API函數(shù)末秃。

struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num)
功能:獲取設(shè)備資源
參數(shù):dev:設(shè)備對(duì)象

     type:要獲取的資源類型概页,和resource中的flags對(duì)應(yīng)。

               IORESOURCE_MEM:內(nèi)存資源练慕,也包括IO內(nèi)存惰匙,

                 IORESOURCE_IRQ:中斷資源

                 IORESOURCE_DMA :DMA通道資源

  num:第幾個(gè)資源

返回值:成功返回獲取的資源結(jié)構(gòu)體地址。

有人會(huì)不會(huì)有這樣的疑問(wèn)铃将,驅(qū)動(dòng)不是分為字符設(shè)備项鬼、塊設(shè)備、網(wǎng)絡(luò)設(shè)備嗎劲阎?platform設(shè)備又是什么绘盟?這里不要把兩種搞混淆了,驅(qū)動(dòng)仍是只有字符設(shè)備悯仙、塊設(shè)備和網(wǎng)絡(luò)設(shè)備三類龄毡。平臺(tái)設(shè)備只是為了符合linux的驅(qū)動(dòng)模型而產(chǎn)生的,除了平臺(tái)設(shè)備模型雁比,還有I2C設(shè)備模型稚虎、SPI設(shè)備模型。都是為了符合驅(qū)動(dòng)模型而創(chuàng)建的偎捎。但i2c設(shè)備和spi設(shè)備仍然是字符設(shè)備蠢终。

看個(gè)例程把,先來(lái)簡(jiǎn)單熟悉以下過(guò)程

設(shè)備茴她,資源中我們假定一個(gè)寄存器地址

include <linux/init.h>

include <linux/module.h>

include <linux/kernel.h>

include <linux/platform_device.h>

include <linux/ioport.h>

MODULE_LICENSE("GPL");
void demo_release(struct device *dev)
{
printk("%s,%d\n", func, LINE);
}
struct resource res = {
.start = 0x10004000,
.end = 0x10004007,
.flags = IORESOURCE_MEM,
};

struct platform_device virtual_dev = {
.name = "vir_dev", //名稱和驅(qū)動(dòng)中匹配寻拂。
.id = -1,
.dev = {
.release = demo_release,
},
.num_resources = 1,
.resource = &res,
};

static int __init demo_device_init(void)
{
platform_device_register(&virtual_dev);
printk(KERN_INFO"%s,%d\n",func,LINE);
return 0;
}

static void __exit demo_device_exit(void)
{
platform_device_unregister(&virtual_dev);
printk(KERN_INFO"%s,%d\n",func,LINE);
}

module_init(demo_device_init);
module_exit(demo_device_exit);
驅(qū)動(dòng)中,獲得資源丈牢,并打印開(kāi)始和結(jié)束地址

include <linux/init.h>

include <linux/module.h>

include <linux/kernel.h>

include <linux/platform_device.h>

include <linux/errno.h>

include <linux/ioport.h>

MODULE_LICENSE("GPL");
struct resource *res0;
static int virtual_probe(struct platform_device *pdev)
{
printk(KERN_INFO"%s,%d\n",func,LINE);
res0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
printk(KERN_INFO"startaddr:%x,endaddr:%x\n",(unsigned int)res0->start,(unsigned int)res0->end);
return 0;
}

static int virtual_remove(struct platform_device *pdev)
{
printk(KERN_INFO"%s,%d\n",func,LINE);
return 0;
}
struct platform_driver virtual_driver = {
.probe = virtual_probe,
.remove = virtual_remove,
.driver = {
.name = "vir_dev", //名稱和設(shè)備中保持一致
.owner = THIS_MODULE,
}
};
module_platform_driver(virtual_driver);
最后一行是模塊入口函數(shù)和出口函數(shù)的簡(jiǎn)寫(xiě)方式祭钉,這一行就可以代替之前的一堆,這是因?yàn)榧号妫俺跏蓟墓ぷ魑覀儗⒍紩?huì)放到probe函數(shù)中慌核,所以xxx_init()函數(shù)除了作為入口函數(shù)以及注冊(cè)驅(qū)動(dòng)外也就沒(méi)什么作用了距境,所以才有此簡(jiǎn)寫(xiě)方式。
加載兩個(gè)模塊執(zhí)行結(jié)果

注冊(cè)設(shè)備號(hào)垮卓,注冊(cè)字符設(shè)備等工作我們放到probe函數(shù)中去操作垫桂。其余部分和我們之前介紹的字符設(shè)備驅(qū)動(dòng)都一樣了∷诎矗基本的框架就是這樣诬滩,下一節(jié)我們來(lái)看看如何在實(shí)際的開(kāi)發(fā)板中利用platform設(shè)備點(diǎn)亮LED燈
————————————————
版權(quán)聲明:本文為CSDN博主「念念有余」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議灭将,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明疼鸟。
原文鏈接:https://blog.csdn.net/u012142460/article/details/79125461

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市庙曙,隨后出現(xiàn)的幾起案子空镜,更是在濱河造成了極大的恐慌,老刑警劉巖矾利,帶你破解...
    沈念sama閱讀 222,865評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姑裂,死亡現(xiàn)場(chǎng)離奇詭異馋袜,居然都是意外死亡男旗,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)欣鳖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)察皇,“玉大人,你說(shuō)我怎么就攤上這事泽台∈踩伲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,631評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵怀酷,是天一觀的道長(zhǎng)稻爬。 經(jīng)常有香客問(wèn)我,道長(zhǎng)蜕依,這世上最難降的妖魔是什么桅锄? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,199評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮样眠,結(jié)果婚禮上友瘤,老公的妹妹穿的比我還像新娘。我一直安慰自己檐束,他們只是感情好辫秧,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著被丧,像睡著了一般盟戏。 火紅的嫁衣襯著肌膚如雪绪妹。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,793評(píng)論 1 314
  • 那天柿究,我揣著相機(jī)與錄音喂急,去河邊找鬼。 笑死笛求,一個(gè)胖子當(dāng)著我的面吹牛廊移,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播探入,決...
    沈念sama閱讀 41,221評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼狡孔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蜂嗽?” 一聲冷哼從身側(cè)響起苗膝,我...
    開(kāi)封第一講書(shū)人閱讀 40,174評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎植旧,沒(méi)想到半個(gè)月后辱揭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,699評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡病附,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評(píng)論 3 343
  • 正文 我和宋清朗相戀三年问窃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片完沪。...
    茶點(diǎn)故事閱讀 40,918評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡域庇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出覆积,到底是詐尸還是另有隱情听皿,我是刑警寧澤,帶...
    沈念sama閱讀 36,573評(píng)論 5 351
  • 正文 年R本政府宣布宽档,位于F島的核電站尉姨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏吗冤。R本人自食惡果不足惜又厉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望欣孤。 院中可真熱鬧馋没,春花似錦、人聲如沸降传。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,749評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至声旺,卻和暖如春笔链,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背腮猖。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,862評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工鉴扫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人澈缺。 一個(gè)月前我還...
    沈念sama閱讀 49,364評(píng)論 3 379
  • 正文 我出身青樓坪创,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親姐赡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子莱预,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容