參考:
http://blog.csdn.net/qq_28992301/article/details/52385518
http://blog.csdn.net/zoe6553/article/details/6372445
http://blog.chinaunix.net/uid-25014876-id-111745.html
1:什么是platform總線?
platform總線是區別于實體總線USB、 I2C、SPI 、PIC總線的虛擬總線,一些usb設備選址的話需要通過USB總線來進行尋址,
而有些類似于SoC內部外設如led 看門狗 定時器是直接通過內存的尋址空間來進行尋址的,cpu與這些設備通信是不需要總線的,2.6內核以后要
對所有設備進行統一管理,通過kset、kobject來建立層次關系,對這些直接通過內存尋址的設備虛擬了一種總線即platform總線,在硬件上
實際是沒有這個總線;platform內核純軟件的總線,所有的直接通過內存尋址的設備都映射到這條總線上;
2:platform總線的優點
a:可以通過platform總線,可以遍歷所有的platform總線設備;platform本質其實也是kset、kobject,具有kobject的特性
b:實現設備與驅動的分離,通過platform總線,設備與驅動是分開注冊的,通過platform總線的probe來隨時檢測與設備匹配的驅動,如匹配上即進行這個設備的驅動注冊;
c:由于上面這個優勢,一個驅動可以供同類的幾個設備使用;
3:platform總線以及platform總線設備驅動的實現流程
a:platform總線注冊
b:platform_device注冊
c:platform_driver注冊
d:設備與驅動的匹配
e:驅動的注冊
platform總線的工作流程如下圖:
------------------------------------------------------------------------------------------------------------------------------------------------------------------
1:根據上面的流程我們來分析一下具體代碼:
platform總線的注冊:platform的注冊是linux內核工程師已經設注冊好的;重點看一下.match = platform_match函數;platform_driver和platform_device就是通過這個函數來匹配的
1 struct bus_type platform_bus_type = {
2 .name = 'platform',
3 .dev_attrs = platform_dev_attrs,
4 .match = platform_match,
5 .uevent = platform_uevent,
6 .pm = &platform_dev_pm_ops,
7 };
1 int __init platform_bus_init(void)
2 {
3 int error;
4
5 early_platform_cleanup();
6
7 error = device_register(&platform_bus);
8 if (error)
9 return error;
10 error = bus_register(&platform_bus_type);
11 if (error)
12 device_unregister(&platform_bus);
13 return error;
14 }
1 static int platform_match(struct device *dev, struct device_driver *drv)
2 {
3 struct platform_device *pdev = to_platform_device(dev);
4 struct platform_driver *pdrv = to_platform_driver(drv);
5
6 /* match against the id table first */
7 if (pdrv->id_table)
8 return platform_match_id(pdrv->id_table, pdev) != NULL;
9
10 /* fall-back to driver name match */
11 return (strcmp(pdev->name, drv->name) == 0);
12 }
由platform_match_id函數來進行匹配的,如果id_table不為空,則通過id_table來pdev_name匹配,如果為空,則drv->name與pdev->name來進行匹配,
匹配上以后再執行probe函數,這個函數即注冊這個設備的驅動;
---------------------------------------------------------------------------------------------------------------------------------------------------------------
2:platform_device的注冊
在arch/arm/mach-s3c2440/mach-mini2440.c文件中
這里注意.name、.dev.platform_data 這兩個變量
platform_driver和platform_device就是通過name來匹配的。name一致則匹配上;
.dev.platform_data這個元素是中的內容是name、gpio flag def_trigger四個元素
1 static struct platform_device mini2440_led1 = {
2 .name = 's3c24xx_led',
3 .id = 1,
4 .dev = {
5 .platform_data = &mini2440_led1_pdata,
6 },
7 };
8
9 static struct platform_device mini2440_led2 = {
10 .name = 's3c24xx_led',
11 .id = 2,
12 .dev = {
13 .platform_data = &mini2440_led2_pdata,
14 },
15 };
設置好platform_device 結構體以后就可以注冊platform_device設備了,把我們設置好的platform_device結構體放到mini2440這個結構體數組指針中;
1 static struct platform_device *mini2440_devices[] __initdata = {
2 &s3c_device_ohci,
3 &s3c_device_wdt,
4 &s3c_device_i2c0,
5 &s3c_device_rtc,
6 &s3c_device_usbgadget,
7 &mini2440_device_eth,
8 &mini2440_led1,
9 &mini2440_led2,
10 &mini2440_led3,
11 &mini2440_led4,
12 &mini2440_button_device,
13 &s3c_device_nand,
14 &s3c_device_sdi,
15 &s3c_device_iis,
16 &mini2440_audio,
17 };
在arch/arm/mach-s3c2440/mach-mini2440.c
mini2440_init 函數下
platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
使用的platform_add_devices這個函數把mini2440的所有設備注冊到內核中;內核會自動查找platform_device鏈表以及platform_driver鏈表,當match以后字自動執行platform_driver的probe函數;
在整理一下platform_device的注冊過程:
1:設置好platform_device結構體(對于led驅動來說關鍵是name、dev->platform_data兩個元素)
2:初始化好dev->platform_data結構體,這里主要涉及到led驅動所要用到的gpio,
這里我們可以看到linux內核platform驅動框架的設計思想:首先設備和驅動是分開的,同類設備有共性的部分,不同的部分,不同的部分在初始化的即被設置好;共性的部分內核工程師以及設置好;然后在通過一個匹配函數如果內核鏈表的設備與驅動鏈表的驅動匹配,則會自動安裝驅動,否則不會安裝驅動;
3:把設置好的platform_device設備加入到mini2440_devices中
4:在mini2440_device初始化的時候通過platform_add_devices函數把platform設備注冊上去;注冊以后再/sys/bus/platform/devices目錄下會看到dev.name的文件夾
---------------------------------------------------------------------------------------------------------------------------------------------------------
3:platform_driver的注冊
1 struct platform_driver {
2 int (*probe)(struct platform_device *);
3 int (*remove)(struct platform_device *);
4 void (*shutdown)(struct platform_device *);
5 int (*suspend)(struct platform_device *, pm_message_t state);
6 int (*resume)(struct platform_device *);
7 struct device_driver driver;
8 const struct platform_device_id *id_table;
9 };
1 static struct platform_driver s3c24xx_led_driver = {
2 .probe = s3c24xx_led_probe,
3 .remove = s3c24xx_led_remove,
4 .driver = {
5 .name = 's3c24xx_led',
6 .owner = THIS_MODULE,
7 },
8 };
9
10 static int __init s3c24xx_led_init(void)
11 {
12 return platform_driver_register(&s3c24xx_led_driver);
13 }
設置好platform_driver 結構體,使用platform_driver_register注冊即可,這里關鍵的是probe、remove、driver.name 三個變量;
platform_driver_register 使用這個函數注冊以后再 /sys/bus/platform/drivers目錄下會看到 dev.name的文件夾
內核會自動檢測匹配以后會自動執行probe函數;
-----------------------------------------------------------------------------------------------------------------------------------------------------------
代碼實戰:
led_driver.c driver注冊;
1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 12 #include 13 #include 14 #include 15 #include 16 #include 17 #include 18 #include 19 20 struct led_classdev *led_device; 21 struct s5pv210_led_platdata *pdata; 22 23 24 #define x210_led_on 0 25 #define x210_led_off 1 26 27 static void s5pv210_led_set(struct led_classdev *led_cdev, 28 enum led_brightness value) 29 { 30 31 //真正控制硬件的函數 32 if (value == LED_OFF) { 33 gpio_set_value(pdata->gpio, x210_led_off); 34 printk(KERN_INFO 'LED1 OFF...'); 35 } 36 else { 37 38 gpio_set_value(pdata->gpio, x210_led_on); 39 printk(KERN_INFO 'LED1 ON...'); 40 } 41 42 } 43 44 45 46 47 // 模塊安裝函數 48 static int s5pv210_led_probe(struct platform_device *dev) 49 { 50 int ret = -1; 51 printk(KERN_INFO 'led_device initn'); 52 53 54 led_device = kzalloc(sizeof(struct led_classdev), GFP_KERNEL); 55 if (led_device == NULL) 56 { 57 printk(KERN_ERR 'No memory for led_devicen'); 58 return -ENOMEM; 59 } 60 61 pdata = dev->dev.platform_data; 62 63 led_device->name = pdata->name; 64 led_device->brightness_set = s5pv210_led_set; 65 66 67 68 //在這里進行注冊驅動; 69 ret = led_classdev_register(NULL, led_device);
上一篇:tiny210裸機第1課(啟動原理)
下一篇:linux下的misc設備led示例
推薦閱讀最新更新時間:2025-04-15 20:14


設計資源 培訓 開發板 精華推薦
- TDA7265 單電源典型應用電路,帶靜音/待機的 25 +25W 立體聲放大器
- DC1705C-C,具有 LTC6946 超低噪聲和雜散整數 N 頻率合成器和集成 VCO 的演示板
- RT6150A 電流模式降壓-升壓轉換器的典型應用
- 激光測距模塊集成板
- #第七屆立創電賽#電流電壓表
- 用于 ADC 驅動器的 ADA4841-2YRZ 低功耗、低噪聲運算放大器的典型應用電路原理圖
- FRDM-BC3770-EVM,用于帶KL25Z的BC3770電池充電器板的Freedom擴展板
- 帶有分流應用電路檢測電流電路的單相電能表
- LTC1433 超低輸出紋波 5V 至 -1.25V MR 前置放大器電源的典型應用電路
- LT3091IR 簡單電纜壓降補償的典型應用
- 2025上海車展即將啟幕,高通攜手汽車生態伙伴帶來駕駛輔助和艙駕創新成果
- 2025Medtec預登記全面啟動 全鏈創新技術引爆國產替代及出海浪潮
- MEMS傳感器振動檢測技術:現狀、挑戰與解決方案
- MEMS傳感器振動應用技術綜述:原理、方法與發展趨勢
- 從零入門:工業信號調節器的類型(模擬數字)與隔離器(光耦磁耦)對比
- 高精度工業信號調節器:低噪聲放大器與校準電路的實現
- 工業4.0下的信號調節器,物聯網、邊緣計算與遠程監控的融合
- 伊士曼中間膜“黑科技”玩轉舒適智駕
- 逐點半導體與PerfDog達成合作,為游戲性能測評提供多維度數據支撐
- 破解汽車與工業等應用新挑戰,TDK展示多傳感器融合與AI+發展趨勢