Linux设备驱动模型4——平台总线实践
以下內(nèi)容源于朱有鵬《物聯(lián)網(wǎng)大講堂》課程的學(xué)習(xí),如有侵權(quán),請(qǐng)告知?jiǎng)h除。
參考http://www.cnblogs.com/deng-tao/p/6033571.html
一、平臺(tái)總線(xiàn)實(shí)踐環(huán)節(jié)1
(編寫(xiě)X210下基于平臺(tái)總線(xiàn)的LED驅(qū)動(dòng),本節(jié)把上個(gè)課程的LED驅(qū)動(dòng)源碼拿來(lái)改寫(xiě)成平臺(tái)總線(xiàn)制式,先實(shí)現(xiàn)platform_driver。)
1、回顧
2、先初步改造添加platform_driver
(1)第1步:先修改原來(lái)的代碼到只有l(wèi)ed1
(2)第2步:將init的改到probe函數(shù)里。
3、實(shí)驗(yàn)現(xiàn)象分析
- 在/sys/bus/platform/driver下有此驅(qū)動(dòng)(見(jiàn)圖),因?yàn)轵?qū)動(dòng)程序有如下片段。
- 但probe函數(shù)不會(huì)被執(zhí)行,因?yàn)檫@里只是driver單方面的注冊(cè),沒(méi)有設(shè)備。
static int __init s5pv210_led_init(void) {return platform_driver_register(&s5pv210_led_driver); }
二、平臺(tái)總線(xiàn)實(shí)踐環(huán)節(jié)2
(分析系統(tǒng)移植時(shí)的mach文件,然后找到并添加LED相關(guān)的platform_device的注冊(cè)。)
1、檢查mach-x210.c中是否有l(wèi)ed相關(guān)的platform_device
- 查找platform_device數(shù)組,發(fā)現(xiàn)沒(méi)有。
2、因此參考mach-mini2440.c中添加led的platform_device定義
(1)先寫(xiě)此設(shè)備的結(jié)構(gòu)體
- a、先寫(xiě)頭文件(主要是struct s5pv210_led_platdata這個(gè)結(jié)構(gòu)體的聲明)
- b、實(shí)例化struct s5pv210_led_platdata這個(gè)結(jié)構(gòu)體(注意修改.flags,截圖的沒(méi)有改)
- c、最后寫(xiě)此設(shè)備的結(jié)構(gòu)體
(2)再添加到platform_device數(shù)組中
3、測(cè)試只有platform_device沒(méi)有platform_driver時(shí)是怎樣的?
- 因?yàn)闆](méi)有加載driver,因此只有platform_device(它不用加載,而是集成到內(nèi)核中)
三、平臺(tái)總線(xiàn)實(shí)踐環(huán)節(jié)3
(本節(jié)同時(shí)調(diào)試platform_device和platform_driver,進(jìn)行問(wèn)題排除和代碼測(cè)試、講解。)
1、測(cè)試platform_device和platform_driver相遇時(shí)會(huì)怎樣?
- 設(shè)備已經(jīng)集成到內(nèi)核,只要加載驅(qū)動(dòng)就好。
- dplatform_driver只要裝載,按理應(yīng)該執(zhí)行dplatform_driver的probe函數(shù)。(驗(yàn)證方法,通過(guò)在probe函數(shù)中添加printk信息驗(yàn)證)
現(xiàn)象1:
分析:
(1)三個(gè)led的設(shè)備名字一樣,按理都能匹配上的。
(2)第一次打印出來(lái)的,應(yīng)該是正確的。
(3)第二次打印出來(lái)的,是失敗的,因?yàn)閘ed1申請(qǐng)的gpio資源,led2又申請(qǐng)相同的資源。
現(xiàn)象2:
2、probe函數(shù)
(1)probe函數(shù)應(yīng)該做什么
(2)probe函數(shù)的數(shù)據(jù)從哪里來(lái)
(3)編程實(shí)踐
四、平臺(tái)總線(xiàn)實(shí)踐環(huán)節(jié)4
(硬件配置信息中的數(shù)據(jù)如何從device端傳遞到driver端,并且被driver接收用于硬件的操作方法中。)
(1)兩個(gè)接口函數(shù):platform_device_register和platform_driver_register
-------------------------------------------------轉(zhuǎn)載文————————
PLATFORM總線(xiàn)驅(qū)動(dòng)代碼分析
/************************************************************************/
Linux內(nèi)核版本:2.6.35.7
運(yùn)行平臺(tái):三星s5pv210
/************************************************************************/
?
1、本例中通過(guò)使用Linux驅(qū)動(dòng)模型中的platform總線(xiàn)和led驅(qū)動(dòng)框架編寫(xiě)出來(lái)的led驅(qū)動(dòng)代碼來(lái)分析platform總線(xiàn)的工作原理,本代碼是我自己將近快一天的時(shí)間編寫(xiě)出來(lái)的,
已經(jīng)通過(guò)運(yùn)行驗(yàn)證代碼是能夠正常運(yùn)行的。對(duì)此對(duì)代碼做如下分析:
在platform總線(xiàn)(任意總線(xiàn))下的驅(qū)動(dòng)代碼都是要分為兩部分:設(shè)備和驅(qū)動(dòng),在platform總線(xiàn)下是platform_device和platform_driver。
關(guān)于這個(gè)問(wèn)題,我在我的上一篇博客中已經(jīng)做了很好的說(shuō)明。對(duì)于設(shè)備部分的注冊(cè):
(1)一般是內(nèi)核的移植工程師在做移植的時(shí)候添加的,在我的這個(gè)移植好的內(nèi)核中是放在arch/arm/mach-s5pv210/mach-x210.c文件中,
所以如果移植工程師沒(méi)有添加你需要編寫(xiě)的驅(qū)動(dòng)對(duì)應(yīng)的設(shè)備,那么就需要你自己添加,你可以直接在這個(gè)文件中添加,系統(tǒng)啟動(dòng)的時(shí)候就會(huì)
直接加載這個(gè)文件的代碼,所以設(shè)備就會(huì)被注冊(cè)到我們的platform總線(xiàn)下,那么就會(huì)添加到platform總線(xiàn)管理下的device設(shè)備管理相關(guān)的數(shù)
據(jù)結(jié)構(gòu)中去(鏈表),此時(shí)platform總線(xiàn)下的match函數(shù)就會(huì)自動(dòng)進(jìn)行匹配(每注冊(cè)一個(gè)設(shè)備或者驅(qū)動(dòng)match函數(shù)都會(huì)被調(diào)用),因?yàn)榇藭r(shí)還
相應(yīng)的驅(qū)動(dòng)被注冊(cè),所以匹配肯定是失敗的;當(dāng)我們把驅(qū)動(dòng)也注冊(cè)之后,也會(huì)把驅(qū)動(dòng)添加到platform總線(xiàn)管理下的drive驅(qū)動(dòng)管理相關(guān)的數(shù)據(jù)
結(jié)構(gòu)中去(也是一個(gè)鏈表),platform總線(xiàn)將會(huì)再次執(zhí)行match函數(shù),此時(shí)match函數(shù)就會(huì)匹配成功,platform總線(xiàn)下的設(shè)備和驅(qū)動(dòng)就建立了對(duì)應(yīng)
關(guān)系了,那么設(shè)備就能夠工作了。
(2)設(shè)備注冊(cè)部分也可以單獨(dú)編寫(xiě), 我們下驅(qū)動(dòng)的時(shí)候提供 xxxxx_device.c(用來(lái)編寫(xiě)設(shè)備部分)和xxx_driver(用來(lái)編寫(xiě)驅(qū)動(dòng)部分),將他們
編譯成模塊,系統(tǒng)啟動(dòng)之后分別使用insmod裝載設(shè)備和驅(qū)動(dòng)(順序無(wú)所謂)。這種情況一般使用在調(diào)試階段,如果確定我們的驅(qū)動(dòng)是沒(méi)有bug的情況下
,最好還是把驅(qū)動(dòng)編譯進(jìn)內(nèi)核,把他們放在他們應(yīng)該在的位置。
?
2、led驅(qū)動(dòng)代碼
本例子采用的是單獨(dú)編寫(xiě)編譯的方式,代碼分析如下:
(1)設(shè)備部分: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 // 定義一個(gè)結(jié)構(gòu)體用于放置本設(shè)備的私有數(shù)據(jù) 13 struct x210_led_platdata { 14 unsigned int gpio; // led設(shè)備用到的GPIO 15 char *device_name; // led設(shè)備在/sys/class/leds/目錄下的名字 16 char *gpio_name; // 使用gpiolob申請(qǐng)gpio資源時(shí)分配的名字 17 }; 18 19 20 // 定義x210_led_platdata類(lèi)型的變量,分別對(duì)應(yīng)板子上的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個(gè)release函數(shù),當(dāng)我們卸載設(shè)備時(shí)會(huì)調(diào)用platform_device結(jié)構(gòu)體中的device結(jié)構(gòu)體下的release函數(shù) 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個(gè)platform_device結(jié)構(gòu)體 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個(gè)platform_device結(jié)構(gòu)體地址放入一個(gè)數(shù)組中 107 static struct platform_device *x210_devices[] = { 108 &x210_led1, 109 &x210_led2, 110 &x210_led3, 111 &x210_led4, 112 }; 113 114 // 入口函數(shù) 115 static int __init leds_x210_init(void) 116 { 117 if (platform_add_devices(x210_devices, ARRAY_SIZE(x210_devices))) // 循環(huán)注冊(cè)platform平臺(tái)設(shè)備 118 { 119 printk(KERN_ERR "platform_add_devices failed.\n"); 120 return -1; 121 } 122 123 return 0; 124 } 125 126 // 出口函數(shù)(卸載) 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]); // 當(dāng)執(zhí)行到這個(gè)函數(shù)的時(shí)候就會(huì)去執(zhí)行platform_device結(jié)構(gòu)體中的device結(jié)構(gòu)體下的release函數(shù) 132 } 133 134 /*函數(shù)入口和出口修飾*/ 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)驅(qū)動(dòng)部分: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結(jié)構(gòu)體反推得到x210_platform結(jié)構(gòu)體 37 38 if (0 < brightness) 39 gpio_set_value(pdata->gpio, LED_ON_); // 使led小燈發(fā)亮 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指針指向系統(tǒng)分配的地址空間 53 if (NULL == pform) { 54 printk(KERN_ERR "kzalloc failed.\n"); 55 return -ENOMEM; 56 } 57 58 platform_set_drvdata(dev, pform); // 將pform值寫(xiě)入到傳進(jìn)來(lái)的platform_device結(jié)構(gòu)體中的device結(jié)構(gòu)體下的私有數(shù)據(jù)區(qū)中去,以便后面釋放內(nèi)存時(shí)用 59 60 //驅(qū)動(dòng)框架接口填充 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 //注冊(cè)led驅(qū)動(dòng) 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管理器申請(qǐng)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 // 設(shè)置GPIO輸出高電平,關(guān)閉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結(jié)構(gòu)體中的device結(jié)構(gòu)體中的私有數(shù)據(jù)區(qū)的x210_platform指針 101 102 // 卸載led驅(qū)動(dòng) 103 led_classdev_unregister(&pform->led_class); 104 105 // 關(guān)閉所有l(wèi)ed 106 gpio_set_value(platdata->gpio, LED_OFF_); 107 108 // 釋放申請(qǐng)的GPIO資源 109 gpio_free(platdata->gpio); 110 111 // 釋放內(nèi)存 112 kfree(pform); // 這個(gè)一定要放在最后 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 /*函數(shù)入口和出口*/ 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總線(xiàn)工作原理
(1)insmod之后的現(xiàn)象
當(dāng)我們執(zhí)行第一個(gè)insmod的時(shí)候,并沒(méi)有出現(xiàn)后面的4條打印信息,只是執(zhí)行了leds_x210_init函數(shù)(一次insmod只能對(duì)應(yīng)執(zhí)行一次xxx_init函數(shù),
一次rmmod只能對(duì)應(yīng)執(zhí)行一次xxx_exit函數(shù)),這里并沒(méi)有放置打印語(yǔ)句的信息,因?yàn)榇藭r(shí)匹配并不會(huì)成功,所以不會(huì)執(zhí)行驅(qū)動(dòng)層probe函數(shù);
而當(dāng)執(zhí)行第二個(gè)insmod的時(shí)候,此時(shí)platform總線(xiàn)下的match函數(shù)匹配就會(huì)成功,因?yàn)閷?duì)應(yīng)有4個(gè)led設(shè)備,所以就會(huì)匹配4次,執(zhí)行了4次probe
函數(shù)打印出相應(yīng)的信息。
(2)rmmod時(shí)的現(xiàn)象
當(dāng)我們卸載調(diào)驅(qū)動(dòng)或者是設(shè)備中的任何一個(gè),都會(huì)執(zhí)行驅(qū)動(dòng)層的remove函數(shù);如果是先rmmod設(shè)備,那么先執(zhí)行驅(qū)動(dòng)層中的remove函數(shù),之后就
會(huì)執(zhí)行設(shè)備層中的platform_device結(jié)構(gòu)體中的device結(jié)構(gòu)體下的release函數(shù),每一個(gè)設(shè)備都會(huì)執(zhí)行一次,因?yàn)檫@里卸載了4個(gè)設(shè)備,所以就會(huì)執(zhí)行
4次;如果是先rmmod驅(qū)動(dòng),那么直接就會(huì)執(zhí)行驅(qū)動(dòng)層的remove函數(shù)。
(3)總結(jié):
insmod注冊(cè)執(zhí)行入口函數(shù)leds_x210_init -> platform總線(xiàn)下match函數(shù)進(jìn)行匹配 -> 匹配成功則執(zhí)行驅(qū)動(dòng)層platform_driver結(jié)構(gòu)體下的probe函數(shù)(失敗就沒(méi)什么可說(shuō)的了)->
初始化驅(qū)動(dòng)。
rmmod卸載執(zhí)行出口函數(shù)leds_x210_exit?-> 執(zhí)行驅(qū)動(dòng)層platform_driver結(jié)構(gòu)體下的remove函數(shù) -> 如果是設(shè)備則還需要執(zhí)行設(shè)備層platform_device結(jié)構(gòu)體中的device結(jié)構(gòu)體下的
release函數(shù)。
(4)當(dāng)我們注冊(cè)了設(shè)備之后,在platform總線(xiàn)的device中會(huì)出現(xiàn)我們注冊(cè)的設(shè)備;當(dāng)我們注冊(cè)了驅(qū)動(dòng)之后,在platform總線(xiàn)的driver中會(huì)出現(xiàn)驅(qū)動(dòng)名;
但是只要match函數(shù)沒(méi)有匹配成功就不會(huì)執(zhí)行probe函數(shù),那么屬于操作led驅(qū)動(dòng)框架下的接口/sys/class/leds下的設(shè)備接口就不會(huì)出現(xiàn),因?yàn)?/span>
led_classdev_register函數(shù)在probe中執(zhí)行。
總結(jié)
以上是生活随笔為你收集整理的Linux设备驱动模型4——平台总线实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux设备驱动模型2——总线式设备驱
- 下一篇: Java对接某地联通API接口02---