日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux驱动模型开发——linux platform总线机制讲解与实例开发

發布時間:2025/6/15 linux 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux驱动模型开发——linux platform总线机制讲解与实例开发 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1、概述:

通常在Linux中,把SoC系統中集成的獨立外設單元(如:I2C、IIS、RTC、看門狗等)都被當作平臺設備來處理。

從Linux2.6起,引入了一套新的驅動管理和注冊機制:Platform_devicePlatform_driver,來管理相應設備。

Linux中大部分的設備驅動,都可以使用這套機制,設備用platform_device表示,驅動用platform_driver進行注冊。

Linux platform driver機制和傳統的device_driver機制相比,一個十分明顯的優勢在于platform機制將本身的資源注冊進內核,由內核統一管理,在驅動程序中使用這些資源時通過platform_device提供的標準接口進行申請并使用。

這樣提高了驅動和資源管理的獨立性,并且擁有較好的可移植性和安全性。 platform是一個虛擬的地址總線,相比pci,usb,它主要用于描述SOC上的片上資源,比如s3c2410上集成的控制器(lcd,watchdog,rtc等),platform所描述的資源有一個共同點,就是可以在cpu的總線上直接取址。
2、platform機制分為三個步驟 1)總線注冊階段: 內核啟動初始化時的main.c文件中的 kernel_init() ->do_basic_setup() -> driver_init() ->platform_bus_init() ->bus_register(&platform_bus_type),注冊了一條platform總線(虛擬總線) platform總線用于連接各類采用platform機制的設備,此階段無需我們修改,由linux內核維護。 2)添加設備階段 設備注冊的時候 Platform_device_register() -> platform_device_add() -> pdev->dev.bus = &platform_bus_type -> device_add(),就這樣把設備給掛到虛擬的總線上。 本階段是增加設備到plartform總線上,我們增加硬件設備,需要修改此處信息。? 此部分操作一般arm/arm/mach-s3c2440/mach-smdk2440.c類似的文件中,需要我們根據硬件的實際需要修改相應代碼 3)驅動注冊階段: ?? ?Platform_driver_register() ->driver_register() -> bus_add_driver() -> driver_attach() ->bus_for_each_dev(),
對在每個掛在虛擬的platform bus的設備作__driver_attach() ->driver_probe_device()
?? ?判斷drv->bus->match()是否執行成功,此時通過指針執行platform_match-> strncmp(pdev->name , drv->name , BUS_ID_SIZE),如果相符就調用really_probe(實際就是執行相應設備的platform_driver->probe(platform_device)。)
開始真正的探測,如果probe成功,則綁定設備到該驅動。
本階段是在編寫具體的驅動程序時完成,在注冊驅動時獲取步驟2中已經申請的資料,一般由程序開發者完成。

3、platform設備開發過程 ?? ?platform機制開發的并不復雜,由兩部分組成:platform_device和platfrom_driver ?? ?通過Platform機制開發發底層驅動的大致流程為:?
?? ?定義 platform_device
?? ?注冊 platform_device ?? ?定義 platform_driver ?? ?注冊 platform_driver ???以linux2.6.34平臺下S3C2440為例:前兩項工作主要在arch/arm/mach-s3c2440/match-smdk2440.c與arch/arm/platform-s3c24xx/devs.c中完成,后兩項工作主要在編寫具體的驅動程序時完成
在2.6內核中platform設備用結構體platform_device來描述,該結構體定義在kernel\include\linux\platform_device.h中,
struct platform_device
?{
?? ? const char * name;
?? ? u32 ?id;
?? ? struct device dev;
?? ? u32 ?num_resources;
?? ? struct resource * resource;
};
每個具體的驅動都對應一個這樣的結構體。?
Platform_device結構體描述了一個platform結構的設備,在其中包含了 一般設備的結構體:struct device ?dev; 設備的資源結構體:struct resource * resource; 還有設備的名字:const char * name。
(注意,這個名字一定要和后面platform_driver.driver->name相同,因為在注冊具體的設備驅動時會遍歷這個結構體查找相應的數據結構,后面會詳細講解)

該結構一個重要的元素是resource,該元素存入了最為重要的設備資源信息,定義在kernel\include\linux\ioport.h中,
struct resource
?{
?? ?resource_size_t start; ?//定義資源的起始地址
?? ?resource_size_t end; ? ?//定義資源的結束地址
?? ?const char *name; ? ? ? //定義資源的名稱
?? ?unsigned long flags; ? ?//定義資源的類型,比如MEM,IO,IRQ,DMA類型
?? ?struct resource *parent, *sibling, *child; ?//資源鏈表指針
};
主要用于定義具體設備占用的硬件資源(如:地址空間、中斷號等;
其中 flags位表示該資源的類型 start和end分別表示該資源的起始地址和結束地址; 要注意的是,這里的platform_device設備的注冊過程必須在相應設備驅動加載之前被調用; 即執行platform_driver_register()之前,原因是驅動注冊時需要匹配內核中所有已注冊的設備名。

我們以內核中對SMDK2440的支持為例觀察一下整個過程:
內核啟動過程中會調用用 arch/arm/mach-s3c2440/smdk2440_machine_init()函數進行板級硬件初始化
static void __init smdk2440_machine_init(void)
{
s3c24xx_fb_set_platdata(&smdk2440_fb_info);
s3c_i2c0_set_platdata(NULL);
platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
smdk_machine_init();
}
此函數中調用了platform_add_devices() -> platform_device_register()注冊platform設備
注冊順序根據同文件夾下的
static struct platform_device *smdk2440_devices[] __initdata =?
{
&s3c_device_ohci,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_dm9k,
?&s3c24xx_uda134x,
&s3c_device_sdi,
};
結構體進行
這些設備的初始化一般都在arch/arm/plat-s3c24xx/devs.c下
我們以s3c_device_wdt為例進行觀察:

/* Watchdog */
//看門狗資源結構體
static struct resource s3c_wdt_resource[] = {
[0] = {
.start = S3C24XX_PA_WATCHDOG,
.end ? = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,
.flags = IORESOURCE_MEM, //看門狗所使用的IO口范圍
},
[1] = {
.start = IRQ_WDT,
.end ? = IRQ_WDT,
.flags = IORESOURCE_IRQ, ?//看門狗所使用的中斷資源
}
};

//定義了一個看門狗結構體
struct platform_device s3c_device_wdt = {
.name ?= "s3c2410-wdt",??//驅動名稱
.id ?= -1, ? ? ? ? ? ? ? ? ? ? ? ? ? //id號,-1代表自動分配
.num_resources?= ARRAY_SIZE(s3c_wdt_resource),
??//指定資源數量
.resource ??= s3c_wdt_resource, ? ? ? ? ? ? //指定資源結構體
};


platform_driver在具體的硬件設備驅動編寫中完成:
同plartform_device相似,需要定義并實現以下結構體
struct platform_driver ? ? ? ? ? ? ? ? ?(/include/linux/Platform_device.h)
{
?? ? ? 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;
};
Platform_driver結構體描述了一個platform結構的驅動。
其中除了一些函數指針外,還有一個一般驅動的device_driver結構。

/*Watchdog平臺驅動結構體,平臺驅動結構體定義在platform_device.h中,該結構體內的接口函數需要單獨實現*/
static struct platform_driver watchdog_driver =?
{
?? ?.probe ? ? ? = watchdog_probe, ? /*Watchdog探測函數*/
?? ?.remove ? ? ?= __devexit_p(watchdog_remove),/*Watchdog移除函數*/
?? ?.shutdown ? = watchdog_shutdown, /*Watchdog關閉函數*/
?? ?.suspend ? ?= watchdog_suspend, ?/*Watchdog掛起函數*/
?? ?.resume ? ? = watchdog_resume, ? /*Watchdog恢復函數*/
?? ?.driver ? ? =?
?? ?{
?? ? ? ?/*注意這里的名稱一定要和系統中定義平臺設備的地方一致,這樣才能把平臺設備與該平臺設備的驅動關聯起來*/
?? ? ???.name ? = "s3c2410-wdt",
?? ? ? ?.owner ?= THIS_MODULE,
?? ?},
};

static int __init watchdog_init(void)
{
?? ?/*將Watchdog注冊成平臺設備驅動*/
?? ?return platform_driver_register(&watchdog_driver);
}

static void __exit watchdog_exit(void)
{
?? ?/*注銷Watchdog平臺設備驅動*/
?? ?platform_driver_unregister(&watchdog_driver);
}

module_init(watchdog_init);
module_exit(watchdog_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("linux");
MODULE_DESCRIPTION("S3C2440 Watchdog Driver");

static int __devinit watchdog_probe(struct platform_device *pdev)
{
?? ?int ret;
?? ?int started = 0;
?? ?struct resource *res;/*定義一個資源,用來保存獲取的watchdog的IO資源*/
?/*在系統定義的watchdog平臺設備中獲取watchdog中斷號platform_get_irq定義在platform_device.h中*/
?? ?wdt_irqno = platform_get_irq(pdev, 0);

?/*申請Watchdog中斷服務,這里使用的是快速中斷:IRQF_DISABLED。中斷服務程序為:wdt_irq,將Watchdog平臺設備pdev做參數傳遞過去了*/
?? ?ret = request_irq(wdt_irqno, wdt_irq, IRQF_DISABLED, pdev->name, pdev);

/*獲取watchdog平臺設備所使用的IO端口資源,注意這個IORESOURCE_MEM標志和watchdog平臺設備定義中的一致*/
?? ?res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

/*申請watchdog的IO端口資源所占用的IO空間(要注意理解IO空間和內存空間的區別),request_mem_region定義在ioport.h中*/
?? ?wdt_mem = request_mem_region(res->start, res->end - res->start + 1, pdev->name);

/*將watchdog的IO端口占用的這段IO空間映射到內存的虛擬地址,ioremap定義在io.h中。
?? ? 注意:IO空間要映射后才能使用,以后對虛擬地址的操作就是對IO空間的操作,*/

?? ?wdt_base = ioremap(res->start, res->end - res->start + 1);

?? ?return ret;
}

/*Watchdog平臺驅動的設備移除接口函數的實現*/
static int __devexit wdt_remove(struct platform_device *dev)
{
??/*釋放獲取的Watchdog平臺設備的IO資源*/
?? ?release_resource(wdt_mem);
?? ?kfree(wdt_mem);

?? ?wdt_mem = NULL;

?/*同watchdog_probe中中斷的申請相對應,在那里申請中斷,這里就釋放中斷*/
?? ?free_irq(wdt_irqno, dev);
?? ?wdt_irq = NULL;

?/*釋放獲取的Watchdog平臺設備的時鐘*/
?? ?clk_disable(wdt_clock);
?? ?clk_put(wdt_clock);

?? ?wdt_clock = NULL;

?/*釋放Watchdog設備虛擬地址映射空間*/
?? ?iounmap(wdt_base);
?? ?return 0;
}


#ifdef CONFIG_PM

/*定義兩個變量來分別保存掛起時的WTCON和WTDAT值,到恢復的時候使用*/
static unsigned long wtcon_save;
static unsigned long wtdat_save;

/*Watchdog平臺驅動的設備掛起接口函數的實現*/
static int wdt_suspend(struct platform_device *dev, pm_message_t state)
{
?? ?/*保存掛起時的WTCON和WTDAT值*/
?? ?wtcon_save = readl(wdt_base + S3C2410_WTCON);
?? ?wtdat_save = readl(wdt_base + S3C2410_WTDAT);

?? ?/*停止看門狗定時器*/
?? ?wdt_start_or_stop(0);
?? ?return 0;
}

/*Watchdog平臺驅動的設備恢復接口函數的實現*/
static int wdt_resume(struct platform_device *dev)
{
?? ?/*恢復掛起時的WTCON和WTDAT值,注意這個順序*/
?? ?writel(wtdat_save, wdt_base + S3C2410_WTDAT);
?? ?writel(wtdat_save, wdt_base + S3C2410_WTCNT);
?? ?writel(wtcon_save, wdt_base + S3C2410_WTCON);
?? ?return 0;
}

#else /*配置內核時沒選上電源管理,Watchdog平臺驅動的設備掛起和恢復功能均無效,這兩個函數也就無需實現了*/
?? ?#define wdt_suspend NULL
?? ?#define wdt_resume NULL
#endif

總結

以上是生活随笔為你收集整理的linux驱动模型开发——linux platform总线机制讲解与实例开发的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 97香蕉久久夜色精品国产 | 天天狠狠干 | 日韩福利影院 | 精品久久91 | 国产 欧美 自拍 | 日日干日日草 | 国产欧美日韩视频在线观看 | 白嫩情侣偷拍呻吟刺激 | 91美女在线视频 | 免费视频色 | a猛片 | 久久免费的精品国产v∧ | 成人av高清在线观看 | 人人狠狠 | 黄色一级大片在线免费看国产一 | 在线观看特色大片免费网站 | 日韩aa视频| 欧美成人免费高清视频 | 午夜性色| 婷婷玖玖 | aaa毛片视频 | 国产激情视频一区二区 | av资源在线播放 | 免费观看黄色 | 无码人妻精品一区二区蜜桃色欲 | 少妇被按摩师摸高潮了 | www.久久久久 | 潘金莲性xxxxhd | 日本美女动态图 | 久久人妻无码aⅴ毛片a片app | 国产草草视频 | 揄拍成人国产精品视频 | 亚洲啪av永久无码精品放毛片 | 窝窝午夜精品一区二区 | 国产毛片一区二区三区 | 国产91av在线播放 | 天天做天天爱天天爽 | 男人猛进女人爽的大叫 | 毛片黄片免费看 | 蜜臀av性久久久久蜜臀aⅴ麻豆 | 草草影院网址 | 91久久人人| 午夜久久久久久久久久 | www久久久com| 夜夜操网址 | 性做久久久久久免费观看 | aaa色| 五月天丁香婷 | 韩国中文字幕在线观看 | 欧美日韩aaa | 欧美日韩国产一区二区三区 | 免费香蕉视频 | 污导航在线 | 天天射天天干 | 亚洲成人aa| 成人精品视频一区二区 | av免费高清| 国产精品jizz在线观看老狼 | 很嫩很紧直喷白浆h | 九九热精品视频在线播放 | 黄色aa网站| 麻豆精品在线播放 | 少妇性l交大片免潘金莲 | 九九九九九伊人 | 97在线国产 | v片在线免费观看 | 日本aa在线观看 | 欧美做爰xxxⅹ性欧美大片 | 人妻熟女一区二区aⅴ水野 91在线观看视频 | 天堂网2014| 97伊人| 丝袜美腿中文字幕 | 日本成人黄色片 | 在线观看免费大片 | 美女一级黄色片 | 亚洲影院在线观看 | 亚洲精品v天堂中文字幕 | 欧美猛交免费 | 国产www| 激情网站在线 | www.夜夜爽 | 一级a毛片免费观看久久精品 | 老牛影视少妇在线观看 | 亚洲夜夜夜 | 精产国品一区二区三区 | 超碰caoporen | 日本三级黄在线观看 | 亚洲精品喷潮一区二区三区 | 亚洲暴爽 | 久久美| 精品人妻一区二区三区免费 | 亚洲精品一区在线 | 亚洲欧美日韩综合 | 天天干夜夜添 | 一个色av| 久久男人av| 国产黄色激情视频 | 伊人久久在线 | 天天透天天操 |