分離分層結構與平臺裝置驅動
本文根據自己的理解分析了分離分層結構和機制,後面也結合點燈例子分析了平臺裝置驅動,水平有限,
錯誤之處歡迎批評指正。同時也希望能對大家有點幫助。
概念:
分層: 核心層和裝置相關層分開,如輸入子系統分核心層input.c 和裝置相關層 evdev.c ,button.c
分層思想的優點就是能把很多檔案共用的程式碼抽離集中起來成為一個或者多個核心檔案供裝置相關
層呼叫,如輸入子系統中核心層input.c檔案為所有輸入裝置的裝置相關層提供很多共用的函式。
分離: 把裝置相關層中那些與硬體緊密聯絡的程式碼(這些程式碼會根據不同的硬體作修改,不是穩定不
變的程式碼)和驅動(與具體硬體無關的,比較穩定的程式碼)分離開來,即要編寫兩個檔案:dev.c和drv.c。如
輸入子系統中dev.c就是我們自己寫的button.c, drv.c就是evdev.c(雖然名字不是evdrv.c,但這裡這樣理解
還是貼切的,evdev.c是穩定的程式碼,它是標準字元裝置相關的檔案,是系統自帶的,與具體硬體無關的,
不需要我們修改的)。
分離分層是通過 匯流排-裝置-驅動(bus-device-driver) 模型來進行分離分層的。
核心層: bus
裝置相關層: device driver
核心層 bus 與裝置相關層device,driver 進行分層。
裝置層的 device 與 driver 進行分離,使對應的drv.c 與具體硬體無關,這樣同一個drv.c就可以驅動
同種類似的硬體,根據硬體資訊的不同,修改dev.c即可。dev.c的主要是收集硬體資訊來構造device結
構體,然後把這個結構體新增到核心層的dev連結串列中。
bus_type型別結構體bus有dev和drv連結串列,driver有probe成員。
構造好device結構體後,呼叫device_add()把device結構體放入bus的device連結串列中,加入新的
device結 構體後,系統會自動從bus的drv連結串列取出每一個drv,用bus的match函式判斷驅動能否支援這
個dev,若支援就呼叫驅動的probe().
構造好driver結構體後,呼叫driver_register()把driver結構體放入bus 的driver連結串列中,加入新的
driver結構體後系統會自動從bus的dev連結串列取出每一個dev,用bus的match函式判斷dev能否支援這個
drv,若支援就呼叫驅動的probe()。
match函式是根據device的bus_id和driver的name是否一致來匹配的,所以構造這兩個結構體的
時要注意device->bus_id要與driver->name 保持一致。
總結:
當有新的device新增到bus的dev連結串列時,系統就自動根據device->bus_id掃描比較driver->name,
如果一致則匹配,呼叫driver 的probe()函式。
或者當有新的driver新增到bus的drv連結串列時,系統就自動根據driver->name掃描比較device-bus_id,
如果一致則匹配,呼叫driver的probe()函式。
這就是 匯流排-裝置-驅動 模型提供的一種機制,這種機制簡單說就是當系統中有了device 或driver
更新的時候,就會自動掃描比較是否有與之匹配的的driver 或device,有則呼叫driver 的probe()函式,
至於probe()函式裡面幹什麼,完全由我們自己決定,由我們自己現實。
有了這樣一種機制,我們寫驅動的時候就很容易實現分離分層了,把硬體相關的,經常需要修改的
寫在device這一邊(後面其他檔案需要用到硬體相關的資訊的話都由device結構體提供),把比較穩定的
程式碼寫在driver這一邊,把想在他們匹配之後要做的事寫在driver 的probe()裡面。
platform裝置驅動:
平臺匯流排-裝置-驅動 模型是由 匯流排-裝置-驅動 模型衍生出的,它的裝置和驅動結構都內嵌了device
和driver結構體,本質上沒有什麼區別。下面結合例項分析 平臺匯流排-裝置-驅動 模型。
例子:利用patform匯流排裝置驅動來點亮一個LED。
根據匯流排裝置模型,我們需要編寫兩個檔案led_dev.c和led_drv.c。
led_dev.c 主要負責硬體相關程式碼,platform_device 有一個resource結構體,重點要理解用什麼樣的
硬體資源來構造這個platform_device的resource 結構體陣列,不知怎麼表達,大家體會體會,也不難。
編寫步驟:
1、構造resource結構體陣列
2、分配/設定platform_device
3、註冊patform_device
led_dev.c :
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
/* 構造資源結構體陣列,platform_device需要的很多硬體資源資訊都是通過 resource 結構體獲取的 */
static struct resource led_resource[] = {
[0] = {
.start = 0x56000050, // gpfcon暫存器開始地址
.end = 0x56000050 + 8 - 1, // gpfcon暫存器結束地址
.flags = IORESOURCE_MEM, // 標誌,mem資源
},
[1] = {
.start = 5, // gpfdat 第五bit,對應某個燈。
.end = 5,
.flags = IORESOURCE_IRQ,
}
};
static void led_release(struct device * dev)
{
}
/* 分配/設定一個platform_device */
static struct platform_device led_dev = {
.name = "myled", // 必須和platform_driver內嵌的driver.name一樣
.id = -1,
.num_resources = ARRAY_SIZE(led_resource), // 資源大小
.resource = led_resource, // 前面的資源陣列
.dev = {
.release = led_release, // 必須設定,空的也可以
},
};
/* 註冊一個platform_device */
static int led_dev_init(void)
{
platform_device_register(&led_dev);
return 0;
}
static void led_dev_exit(void)
{
platform_device_unregister(&led_dev);
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
led_drv.c中需要做的事:
1、獲取硬體資源實現裝置的讀寫函式,以便構造操作裝置的fops
2、實現platform_driver的probe()函式和remove()函式
3、分配/設定platform_driver
4、註冊patform_driver
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>
static int major;
static struct class *cls;
static volatile unsigned long *gpio_con;
static volatile unsigned long *gpio_dat;
static int pin;
static int led_open(struct inode *inode, struct file *file)
{
/* 配置為輸出 */
*gpio_con &= ~(0x3<<(pin*2)); //gpio_con 和pin 在 probe()函式中被賦值
*gpio_con |= (0x1<<(pin*2));
return 0;
}
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val;
copy_from_user(&val, buf, count);
if (val == 1) // 點燈
*gpio_dat &= ~(1<<pin); //gpio_dat 和pin 在 probe()函式中被賦值,通過
else // 滅燈 //led_dev.c裡面的led_resource結構體獲得暫存器地址賦給gpio_dat,
*gpio_dat |= (1<<pin); //獲得 5 賦給pin,pin表示第5bit.
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
};
/* 實現platform_driver 的probe()函式,這個函式在platform_device 和platform_driver成功匹配的時候
* 被呼叫,可以在裡面實現自己想要的功能,這裡獲取暫存器地址進行ioremap,獲取相關資源註冊字元裝置,
* 註冊了裝置之後,應用程式就可以操作設別了。
*/
static int led_probe(struct platform_device *pdev)
{
struct resource *res;
/* 根據platform_device的資源進行ioremap */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
gpio_con = ioremap(res->start, res->end - res->start + 1);
gpio_dat = gpio_con + 1;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
pin = res->start;
/* 註冊字元裝置驅動程式 */
printk("led_probe, found led\n");
major = register_chrdev(0, "myled", &led_fops);
cls = class_create(THIS_MODULE, "myled");
class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
return 0;
}
static int led_remove(struct platform_device *pdev) //把probe()裡面註冊,分配,新增或map的東西清理掉。
{
/* 解除安裝字元裝置驅動程式 */
/* iounmap */
printk("led_remove, remove led\n");
class_device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, "myled");
iounmap(gpio_con);
return 0;
}
struct platform_driver led_drv = {
.probe = led_probe, // 匹配後將呼叫的函式
.remove = led_remove, // 與pobe功能相關,做清理工作
.driver = {
.name = "myled", //必須和platform_device的name一樣
}
};
/* 註冊platform_driver */
static int led_drv_init(void)
{
platform_driver_register(&led_drv);
return 0;
}
static void led_drv_exit(void)
{
platform_driver_unregister(&led_drv);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
總結:寫platform裝置驅動的時候要寫兩個檔案,dev.c和drv.c,
在dev.c裡面把所有要用到的硬體資源資訊寫到resource結構體陣列中,然後分配,設定,註冊
platf orm_device.
在drv.c裡面通過resource獲得所需資訊,分配和設定好為註冊字元裝置所需的資源,如構造操作
裝置的fops,再實現platfrom_driver的probe()函式和remove()函式,分配,設定,註冊platfrom_driver,
在probe()函式裡面註冊裝置。
platform裝置驅動模型利用了 匯流排-裝置-驅動 模型的分離分層結構,和匹配成功後自動呼叫probe()
函式的這種機制。
platform裝置驅動模型 較 匯流排-裝置-驅動模型的不同是它在dev.c內部再一次進行分離,把所有與硬
件直接相關的資訊統統寫到resource結構體中,使其他部分能與硬體分離開來,要用到硬體資訊的時候,
通過resource結構體間接獲得。硬體有改動時只修改resource結構體即可,其他部分幾乎無需修改。
相關推薦
分離分層結構與平臺裝置驅動
本文根據自己的理解分析了分離分層結構和機制,後面也結合點燈例子分析了平臺裝置驅動,水平有限, 錯誤之處歡迎批評指正。同時也希望能對大家有點幫助。 概念: 分層: 核心層和裝置相關層分開,如輸入子系統分核心層input.c 和裝置相關層 e
淺析結構體函式指標與核心裝置驅動
最近在公司沒什麼事做,突然有來寫寫日誌和部落格,這種突發的靈感來自於我在學習核心驅動程式碼的時候發現了結構體的一種古老的初始化方法,多虧了尚觀廣州校區的葉老師在群裡用心的給我文字講解,讓我明白了原來結構體初始化也可以用:冒號這樣的方法。 其實在C語言中
Linux中的平臺裝置驅動模型
在 Linux 的裝置驅動模型中,關心匯流排、裝置和驅動這 3 個實體,匯流排將裝置和驅動繫結。在系統每註冊一個裝置的時候,會尋找與之匹配的驅動;相反的,在系統每註冊一個驅動的時候,會尋找與之匹配的裝置,而匹配由匯流排完成。 平臺裝置匹配的依據是: 1
Linux Platform devices 平臺裝置驅動
裝置匯流排驅動模型:http://blog.csdn.net/lizuobin2/article/details/51570196 本文主要參考:http://www.wowotech.net/device_model/platform_device.ht
Web 分層結構與異常處理
常見的Web軟體分層結構 開放介面層:可直接封裝 Service 方法暴露成 RPC 介面; 通過 Web 封裝成 http 介面; 進行閘道器安全控制、 流量控制等。終端顯示層:各個端的模板渲染
字元裝置驅動與塊裝置驅動、網路裝置驅動的區別
在Linux作業系統下有3類主要的裝置檔案型別:塊裝置、字元裝置和網路裝置。這種分類方法可以將控制輸入/輸出裝置的驅動程式與其他作業系統軟體分離開來。字元裝置是指存取時沒有快取的裝置。典型的字元裝置包括滑鼠、鍵盤、序列口等。字元裝置與塊裝置的主要區別是:在對字元裝置發出讀/
Linux Platform平臺裝置驅動模型
從Linux2.6起引入了一套新的驅動管理和註冊模型,即平臺裝置platform_device和平臺驅動platform_driver. Linux中大部分的裝置驅動,都可以使用這套機制,裝置用platform_device表示,驅動用platform_driver表示。平臺裝置模型與傳統的device和d
【總結】裝置樹對platform平臺裝置驅動帶來的變化
最初我們學習裝置樹的時候,第一個例子是按鍵中斷,其採用了裝置樹的方式。我們以此為例分析裝置樹引入對platform平臺驅動的改變。 一、改變與不變 (1)platform_driver的入口函式,仍採用platform_driver_register註冊(不變) sta
平臺裝置驅動和混雜裝置驅動
1. 平臺裝置驅動 在linux2.6以後的裝置驅動模型中,只關心裝置、驅動和匯流排這三個實體,匯流排將裝置驅動繫結。在向系統註冊一個裝置時會由匯流排匹配對應的驅動,相反當向系統註冊一個驅動時由匯流排匹配出對應的裝置。 在linux裝置和驅動通常要掛接在某條總線上,對於II
linux平臺裝置驅動模型
linux平臺裝置介紹 linux2.6以上的裝置驅動模型中,有三大實體:匯流排,裝置和驅動。匯流排負責將裝置和驅動繫結,在系統沒註冊一個裝置的時候,會尋找與之匹配的驅動:相反的,在系統每註冊一個驅動的時候,會尋找與之匹配的裝置,而匹配則由匯流排完成。 一個
平臺裝置驅動開發流程設計
1平臺匯流排概述 2.平臺裝置 3.平臺驅動 4.範例程式 1平臺匯流排概述 1.1平臺匯流排概述 平臺匯流排(Platform bus)是linux2.6核心加入的一種虛擬匯流排,是在linxu系統中最為重要的一種匯流排。 linux系統中除了去
轉載:linux平臺裝置驅動架構詳解 Linux Platform Device and Driver
從Linux 2.6起引入了一套新的驅動管理和註冊機制:Platform_device和Platform_driver。Linux中大部分的裝置驅動,都可以使用這套機制, 裝置用Platform_device表示,驅動用Platform_driver進行註冊。Linux platform driver機制和傳
linux平臺裝置驅動架構詳解 Linux Platform Device and Driver——神文,非常詳細
從Linux 2.6起引入了一套新的驅動管理和註冊機制:Platform_device和Platform_driver。 Linux中大部分的裝置驅動,都可以使用這套機制, 裝置用Platform_device表示,驅動用Platform_driver進行註冊。 Linux platform driver機
【linux】驅動-7-平臺裝置驅動
[toc] --- ## 前言 區分**裝置驅動模型**和**平臺裝置驅動模型**。 **裝置驅動模型** 可以理解為 **匯流排、裝置、驅動**。 **平臺裝置驅動模型** 就是那些 Linux 核心管理沒有物理匯流排(*即是不需要特殊時序控制的裝置*)(*也是Linux核心沒有自動建立相應驅動匯流
Linux驅動之平臺設備驅動模型簡析(驅動分離分層概念的建立)
技術 描述 rst 操作 mem iou 系統 簡單 reg Linux設備模型的目的:為內核建立一個統一的設備模型,從而有一個對系統結構的一般性抽象描述。換句話說,Linux設備模型提取了設備操作的共同屬性,進行抽象,並將這部分共同的屬性在內核中實現,而為需要新添加設備
驅動程式分層分離概念___匯流排裝置驅動模型
在輸入子系統那一章,一個驅動分為上下兩層,現在我們要引入另外一個概念,叫做分層分離概念:在輸入子系統,Input.c中中提供統一的介面,buttons.c專注於硬體相關的程式碼,evdev.c則專注於純
Linux裝置驅動--LCD平臺裝置與驅動(tiny4412)
1 環境與簡介 Host:Ubuntu14.04(64bit) Target:Tiny4412 Kernel:linux-3.5.0 2 平臺裝置 2.1 宣告 extern struct platform_device s5p_device_fim
gendisk,request與bio結構體,以及塊裝置驅動註冊與登出,以及載入與解除安裝
struct bio *bio; bio是這個請求中包含的bio結構體的連結串列,驅動中不宜直接存取這個成員,而應該使用後文將介紹的rq_for_each_bio()。 char *buffer; 指向緩衝區的指標,資料應當被傳送到或者來自這個緩衝區,這個指標是一個核心虛擬地址,可被驅動直接引用。 uns
Linux裝置驅動--LCD平臺裝置與驅動(smdk2440)
1 環境與簡介 Host:Ubuntu14.04(64bit) Target:smdk2440 Kernel:linux-2.6.39.4 類似於《Linux裝置驅動--WDT平臺裝置與驅動》,本文再以LCD為例進行說明。本文的原始碼均來自L
Linux裝置驅動--LCD平臺裝置與驅動(s3c64xx)
1 開發環境 Host:Ubuntu14.04 Target:s3c64xx Kernel:linux-3.18.2 2 平臺裝置 關於裝置樹是如果被載入並解析成裝置節點的,詳見參考資料[1],本文重點分析如何利用裝置節點建立相