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

歡迎訪問 生活随笔!

生活随笔

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

linux

Linux设备驱动模型4——平台总线实践

發布時間:2023/12/20 linux 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux设备驱动模型4——平台总线实践 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

以下內容源于朱有鵬《物聯網大講堂》課程的學習,如有侵權,請告知刪除。

參考http://www.cnblogs.com/deng-tao/p/6033571.html


一、平臺總線實踐環節1

(編寫X210下基于平臺總線的LED驅動,本節把上個課程的LED驅動源碼拿來改寫成平臺總線制式,先實現platform_driver。)

1、回顧

2、先初步改造添加platform_driver

(1)第1步:先修改原來的代碼到只有led1

(2)第2步:將init的改到probe函數里。

3、實驗現象分析

  • 在/sys/bus/platform/driver下有此驅動(見圖),因為驅動程序有如下片段。
  • 但probe函數不會被執行,因為這里只是driver單方面的注冊,沒有設備。

static int __init s5pv210_led_init(void) {return platform_driver_register(&s5pv210_led_driver); }


二、平臺總線實踐環節2

(分析系統移植時的mach文件,然后找到并添加LED相關的platform_device的注冊。)

1、檢查mach-x210.c中是否有led相關的platform_device

  • 查找platform_device數組,發現沒有。

2、因此參考mach-mini2440.c中添加led的platform_device定義

(1)先寫此設備的結構體

  • a、先寫頭文件(主要是struct s5pv210_led_platdata這個結構體的聲明
  • b、實例化struct s5pv210_led_platdata這個結構體(注意修改.flags,截圖的沒有改)

  • c、最后寫此設備的結構體


(2)再添加到platform_device數組中


3、測試只有platform_device沒有platform_driver時是怎樣的?

  • 因為沒有加載driver,因此只有platform_device(它不用加載,而是集成到內核中)



三、平臺總線實踐環節3

(本節同時調試platform_device和platform_driver,進行問題排除和代碼測試、講解。)

1、測試platform_device和platform_driver相遇時會怎樣?

  • 設備已經集成到內核,只要加載驅動就好。
  • dplatform_driver只要裝載,按理應該執行dplatform_driver的probe函數。(驗證方法,通過在probe函數中添加printk信息驗證)

現象1:


分析:

(1)三個led的設備名字一樣,按理都能匹配上的。

(2)第一次打印出來的,應該是正確的。

(3)第二次打印出來的,是失敗的,因為led1申請的gpio資源,led2又申請相同的資源。

現象2:




2、probe函數

(1)probe函數應該做什么

(2)probe函數的數據從哪里來

(3)編程實踐


四、平臺總線實踐環節4

(硬件配置信息中的數據如何從device端傳遞到driver端,并且被driver接收用于硬件的操作方法中。)

(1)兩個接口函數:platform_device_register和platform_driver_register




-------------------------------------------------轉載文————————

PLATFORM總線驅動代碼分析

/************************************************************************/

Linux內核版本:2.6.35.7

運行平臺:三星s5pv210

/************************************************************************/

?

1、本例中通過使用Linux驅動模型中的platform總線和led驅動框架編寫出來的led驅動代碼來分析platform總線的工作原理,本代碼是我自己將近快一天的時間編寫出來的,

已經通過運行驗證代碼是能夠正常運行的。對此對代碼做如下分析:

在platform總線(任意總線)下的驅動代碼都是要分為兩部分:設備和驅動,在platform總線下是platform_device和platform_driver。

關于這個問題,我在我的上一篇博客中已經做了很好的說明。對于設備部分的注冊:

(1)一般是內核的移植工程師在做移植的時候添加的,在我的這個移植好的內核中是放在arch/arm/mach-s5pv210/mach-x210.c文件中,

所以如果移植工程師沒有添加你需要編寫的驅動對應的設備,那么就需要你自己添加,你可以直接在這個文件中添加,系統啟動的時候就會

直接加載這個文件的代碼,所以設備就會被注冊到我們的platform總線下,那么就會添加到platform總線管理下的device設備管理相關的數

據結構中去(鏈表),此時platform總線下的match函數就會自動進行匹配(每注冊一個設備或者驅動match函數都會被調用),因為此時還

相應的驅動被注冊,所以匹配肯定是失敗的;當我們把驅動也注冊之后,也會把驅動添加到platform總線管理下的drive驅動管理相關的數據

結構中去(也是一個鏈表),platform總線將會再次執行match函數,此時match函數就會匹配成功,platform總線下的設備和驅動就建立了對應

關系了,那么設備就能夠工作了。

(2)設備注冊部分也可以單獨編寫, 我們下驅動的時候提供 xxxxx_device.c(用來編寫設備部分)和xxx_driver(用來編寫驅動部分),將他們

編譯成模塊,系統啟動之后分別使用insmod裝載設備和驅動(順序無所謂)。這種情況一般使用在調試階段,如果確定我們的驅動是沒有bug的情況下

,最好還是把驅動編譯進內核,把他們放在他們應該在的位置。

?

2、led驅動代碼

本例子采用的是單獨編寫編譯的方式,代碼分析如下:

(1)設備部分:leds-x210-device.c

1 #include <linux/module.h> // module_init module_exit 2 #include <linux/init.h> // __init __exit 3 #include <linux/fs.h> 4 #include <linux/leds.h> 5 #include <mach/regs-gpio.h> 6 #include <mach/gpio-bank.h> 7 #include <linux/io.h> 8 #include <linux/ioport.h> 9 #include <mach/gpio.h> 10 #include <linux/platform_device.h> 11 12 // 定義一個結構體用于放置本設備的私有數據 13 struct x210_led_platdata { 14 unsigned int gpio; // led設備用到的GPIO 15 char *device_name; // led設備在/sys/class/leds/目錄下的名字 16 char *gpio_name; // 使用gpiolob申請gpio資源時分配的名字 17 }; 18 19 20 // 定義x210_led_platdata類型的變量,分別對應板子上的4顆led小燈 21 static struct x210_led_platdata x210_led1_pdata = { 22 .gpio = S5PV210_GPJ0(3), 23 .device_name = "led1", 24 .gpio_name = "led1-gpj0_3", 25 }; 26 27 static struct x210_led_platdata x210_led2_pdata = { 28 .gpio = S5PV210_GPJ0(4), 29 .device_name = "led2", 30 .gpio_name = "led2-gpj0_4", 31 }; 32 33 static struct x210_led_platdata x210_led3_pdata = { 34 .gpio = S5PV210_GPJ0(5), 35 .device_name = "led3", 36 .gpio_name = "led3-gpj0_5", 37 }; 38 39 static struct x210_led_platdata x210_led4_pdata = { 40 .gpio = S5PV210_GPD0(1), 41 .device_name = "led4", 42 .gpio_name = "led4-gpd0_1", 43 }; 44 45 46 // 定義4個release函數,當我們卸載設備時會調用platform_device結構體中的device結構體下的release函數 47 void x210_led1_release(struct device *dev) 48 { 49 printk(KERN_INFO "x210_led1_release\n"); 50 } 51 52 void x210_led2_release(struct device *dev) 53 { 54 printk(KERN_INFO "x210_led1_release\n"); 55 } 56 57 void x210_led3_release(struct device *dev) 58 { 59 printk(KERN_INFO "x210_led1_release\n"); 60 } 61 62 void x210_led4_release(struct device *dev) 63 { 64 printk(KERN_INFO "x210_led1_release\n"); 65 } 66 67 68 // 定義4個platform_device結構體 69 static struct platform_device x210_led1 = { 70 .name = "x210_led", 71 .id = 0, 72 .dev = { 73 .platform_data = &x210_led1_pdata, 74 .release = x210_led1_release, 75 }, 76 }; 77 78 static struct platform_device x210_led2 = { 79 .name = "x210_led", 80 .id = 1, 81 .dev = { 82 .platform_data = &x210_led2_pdata, 83 .release = x210_led2_release, 84 }, 85 }; 86 87 static struct platform_device x210_led3 = { 88 .name = "x210_led", 89 .id = 2, 90 .dev = { 91 .platform_data = &x210_led3_pdata, 92 .release = x210_led3_release, 93 }, 94 }; 95 96 static struct platform_device x210_led4 = { 97 .name = "x210_led", 98 .id = 3, 99 .dev = { 100 .platform_data = &x210_led4_pdata, 101 .release = x210_led4_release, 102 }, 103 }; 104 105 106 // 將4個platform_device結構體地址放入一個數組中 107 static struct platform_device *x210_devices[] = { 108 &x210_led1, 109 &x210_led2, 110 &x210_led3, 111 &x210_led4, 112 }; 113 114 // 入口函數 115 static int __init leds_x210_init(void) 116 { 117 if (platform_add_devices(x210_devices, ARRAY_SIZE(x210_devices))) // 循環注冊platform平臺設備 118 { 119 printk(KERN_ERR "platform_add_devices failed.\n"); 120 return -1; 121 } 122 123 return 0; 124 } 125 126 // 出口函數(卸載) 127 static void __exit leds_x210_exit(void) 128 { 129 int i = 0; 130 for (i = 0; i < 4; i++) 131 platform_device_unregister(x210_devices[i]); // 當執行到這個函數的時候就會去執行platform_device結構體中的device結構體下的release函數 132 } 133 134 /*函數入口和出口修飾*/ 135 module_init(leds_x210_init); 136 module_exit(leds_x210_exit); 137 138 /*描述模塊信息*/ 139 MODULE_LICENSE("GPL"); // 描述模塊的許可證 140 MODULE_AUTHOR("Tao Deng <> 773904075@qq.com"); // 描述模塊的作者 141 MODULE_DESCRIPTION("led device for x210."); // 描述模塊的介紹信息 142 MODULE_ALIAS("alias DT"); // 描述模塊的別名信息

?

(2)驅動部分:leds-x210-driver.c

1 #include <linux/module.h> // module_init module_exit 2 #include <linux/init.h> // __init __exit 3 #include <linux/fs.h> 4 #include <linux/leds.h> 5 #include <mach/regs-gpio.h> 6 #include <mach/gpio-bank.h> 7 #include <linux/io.h> 8 #include <linux/ioport.h> 9 #include <mach/gpio.h> 10 #include <linux/platform_device.h> 11 #include <linux/slab.h> 12 13 enum LED{ 14 LED_ON_ = 0, 15 LED_OFF_ = 1, 16 }; 17 18 struct x210_led_platdata { 19 unsigned int gpio; 20 char *device_name; 21 char *gpio_name; 22 }; 23 24 struct x210_platform { 25 struct led_classdev led_class; 26 unsigned int gpio; 27 }; 28 29 static inline struct x210_platform *pdev_to_gpio(struct platform_device *dev) 30 { 31 return platform_get_drvdata(dev); 32 } 33 34 static void x210_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) 35 { 36 struct x210_platform *pdata = container_of(led_cdev, struct x210_platform, led_class); // 使用led_classdev結構體反推得到x210_platform結構體 37 38 if (0 < brightness) 39 gpio_set_value(pdata->gpio, LED_ON_); // 使led小燈發亮 40 else 41 gpio_set_value(pdata->gpio, LED_OFF_); // 使led小燈熄滅 42 } 43 44 static int x210_led_probe(struct platform_device *dev) 45 { 46 // 定義變量 47 int ret = 0; 48 struct x210_led_platdata *platdata = dev->dev.platform_data; 49 struct x210_platform *pform = NULL; 50 51 // 分配空間 52 pform = kzalloc(sizeof(struct x210_platform), GFP_KERNEL); // 將pform指針指向系統分配的地址空間 53 if (NULL == pform) { 54 printk(KERN_ERR "kzalloc failed.\n"); 55 return -ENOMEM; 56 } 57 58 platform_set_drvdata(dev, pform); // 將pform值寫入到傳進來的platform_device結構體中的device結構體下的私有數據區中去,以便后面釋放內存時用 59 60 //驅動框架接口填充 61 pform->led_class.name = platdata->device_name; 62 pform->led_class.brightness = 0; 63 pform->led_class.brightness_set = x210_led_set; 64 65 // 保存gpio管腳信息 66 pform->gpio = platdata->gpio; 67 68 //注冊led驅動 69 ret = led_classdev_register(NULL, &pform->led_class); 70 if (ret < 0) { 71 printk(KERN_ERR "led_classdev_register failed.\n"); 72 goto err_led_classdev_register; 73 } 74 75 // 向gpiolib管理器申請gpio資源 76 if (gpio_request(platdata->gpio, platdata->gpio_name)) 77 { 78 printk(KERN_ERR "gpio_request failed.\n"); 79 goto err_gpio_request; 80 } 81 82 // 設置GPIO輸出高電平,關閉LED 83 gpio_direction_output(platdata->gpio, LED_OFF_); 84 85 printk(KERN_INFO "x210_led_probe succeseful.\n"); 86 87 return 0; 88 89 err_gpio_request: 90 led_classdev_unregister(&pform->led_class); 91 92 err_led_classdev_register: 93 94 return -1; 95 } 96 97 static int x210_led_remove(struct platform_device *dev) 98 { 99 struct x210_led_platdata *platdata = dev->dev.platform_data; 100 struct x210_platform *pform = pdev_to_gpio(dev); // 取出platform_device結構體中的device結構體中的私有數據區的x210_platform指針 101 102 // 卸載led驅動 103 led_classdev_unregister(&pform->led_class); 104 105 // 關閉所有led 106 gpio_set_value(platdata->gpio, LED_OFF_); 107 108 // 釋放申請的GPIO資源 109 gpio_free(platdata->gpio); 110 111 // 釋放內存 112 kfree(pform); // 這個一定要放在最后 113 114 printk(KERN_INFO "x210_led_remove succeseful.\n"); 115 116 return 0; 117 } 118 119 static struct platform_driver x210_led_driver = { 120 .probe = x210_led_probe, 121 .remove = x210_led_remove, 122 .driver = { 123 .name = "x210_led", 124 .owner = THIS_MODULE, 125 }, 126 }; 127 128 static int __init leds_x210_init(void) 129 { 130 int ret = 0; 131 132 ret = platform_driver_register(&x210_led_driver); 133 if (ret) 134 printk(KERN_ERR "platform_driver_register failed.\n"); 135 136 return ret; 137 } 138 139 static void __exit leds_x210_exit(void) 140 { 141 platform_driver_unregister(&x210_led_driver); 142 } 143 144 /*函數入口和出口*/ 145 module_init(leds_x210_init); 146 module_exit(leds_x210_exit); 147 148 /*描述模塊信息*/ 149 MODULE_LICENSE("GPL"); // 描述模塊的許可證 150 MODULE_AUTHOR("Tao Deng <> 773904075@qq.com"); // 描述模塊的作者 151 MODULE_DESCRIPTION("led driver for x210."); // 描述模塊的介紹信息 152 MODULE_ALIAS("alias DT"); // 描述模塊的別名信息

?

3、platform總線工作原理

(1)insmod之后的現象

當我們執行第一個insmod的時候,并沒有出現后面的4條打印信息,只是執行了leds_x210_init函數(一次insmod只能對應執行一次xxx_init函數,

一次rmmod只能對應執行一次xxx_exit函數),這里并沒有放置打印語句的信息,因為此時匹配并不會成功,所以不會執行驅動層probe函數;

而當執行第二個insmod的時候,此時platform總線下的match函數匹配就會成功,因為對應有4個led設備,所以就會匹配4次,執行了4次probe

函數打印出相應的信息。

(2)rmmod時的現象

當我們卸載調驅動或者是設備中的任何一個,都會執行驅動層的remove函數;如果是先rmmod設備,那么先執行驅動層中的remove函數,之后就

會執行設備層中的platform_device結構體中的device結構體下的release函數,每一個設備都會執行一次,因為這里卸載了4個設備,所以就會執行

4次;如果是先rmmod驅動,那么直接就會執行驅動層的remove函數。

(3)總結:

insmod注冊執行入口函數leds_x210_init -> platform總線下match函數進行匹配 -> 匹配成功則執行驅動層platform_driver結構體下的probe函數(失敗就沒什么可說的了)->

初始化驅動。

rmmod卸載執行出口函數leds_x210_exit?-> 執行驅動層platform_driver結構體下的remove函數 -> 如果是設備則還需要執行設備層platform_device結構體中的device結構體下的

release函數。

(4)當我們注冊了設備之后,在platform總線的device中會出現我們注冊的設備;當我們注冊了驅動之后,在platform總線的driver中會出現驅動名;

但是只要match函數沒有匹配成功就不會執行probe函數,那么屬于操作led驅動框架下的接口/sys/class/leds下的設備接口就不會出現,因為

led_classdev_register函數在probe中執行。


總結

以上是生活随笔為你收集整理的Linux设备驱动模型4——平台总线实践的全部內容,希望文章能夠幫你解決所遇到的問題。

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