linux設備驅動模型之平臺總線實踐環節(一)
1、首先回顧下之前寫的驅動和數據在一起的led驅動代碼,代碼如下:
#include <linux/module.h> #include <linux/init.h> #include <linux/leds.h> #include <asm/io.h> //ioremap和iounmap的頭文件 writel等 /**********************************靜態映射虛擬地址的方法,用的是內核移植時的靜態映射表**********************************/ #include <mach/regs-gpio.h> #include <mach/gpio-bank.h> #define GPJ0CON S5PV210_GPJ0CON //這個宏是在gpio-bank.h中定義的,是虛擬地址,這個宏還用到了regs-gpio.h中的宏, //以此類推。 #define GPJ0DAT S5PV210_GPJ0DAT static struct led_classdev myled1; static struct led_classdev myled2; static struct led_classdev myled3; //這個函數綁定到了struct led_classdev這個結構體類型的myled變量中的brightness_set成員,當我們用戶寫這個led硬件的時候,因為是用led驅動框架 //寫的led驅動,所以我們是通過led驅動框架中的brightness這個文件,去寫或這個讀led硬件的,應為這個文件在驅動框架中具有了可讀可寫的屬性,是在 //struct //device_attribute結構中擁有的,並且最終綁定了到了這個設備類中。所以最後我們用戶去通過brightness這個屬性文件去寫led硬件的時候,會去執行led_br//ightness_store這個方法,這個方法中,又 //通過調用led_set_brightness這個函數,這個函數中通過led_cdev->brightness_set(led_cdev, value);這條語句,最終對應到了struct //led_classdev結構體中的brightness_set這個函數指針變量,而這個函數指針變量在我們填充那個結構體 //struct led_classdev中的brightness_set函數指針時,就已經綁定好了我們寫的那個寫操作硬件的函數了。所以這一條線就打通了.value就是用戶要寫的值 //value是枚舉,是enum led_brightness類型的枚舉,這個枚舉有三個值,分別是LED_OFF = 0 LED_XXX = 122 LED_FULL = 255, static void whyx210_led1_set(struct led_classdev *led_cdev, enum led_brightness value) { //printk(KERN_INFO "whyx210_led_set\n"); writel(0x11111111, GPJ0CON); if (value == LED_OFF) { //用戶輸入0時滅 對應用戶輸入的是echo 0 > brightness writel(readl(GPJ0DAT) | (1 << 3), GPJ0DAT); }else if (value == LED_FULL){ //用戶輸入255時亮 對應用戶輸入的是echo 255 > brightness writel(readl(GPJ0DAT) & ~(1 << 3), GPJ0DAT); //這樣操作保持其他位值不變 } } static void whyx210_led2_set(struct led_classdev *led_cdev, enum led_brightness value) { //printk(KERN_INFO "whyx210_led_set\n"); writel(0x11111111, GPJ0CON); if (value == LED_OFF) { //用戶輸入0時滅 對應用戶輸入的是echo 0 > brightness writel(readl(GPJ0DAT) | (1 << 4), GPJ0DAT); }else if (value == LED_FULL){ //用戶輸入255時亮 對應用戶輸入的是echo 255 > brightness writel(readl(GPJ0DAT) & ~(1 << 4), GPJ0DAT); //這樣操作保持其他位值不變 } } static void whyx210_led3_set(struct led_classdev *led_cdev, enum led_brightness value) { //printk(KERN_INFO "whyx210_led_set\n"); writel(0x11111111, GPJ0CON); if (value == LED_OFF) { //用戶輸入0時滅 對應用戶輸入的是echo 0 > brightness writel(readl(GPJ0DAT) | (1 << 5), GPJ0DAT); }else if (value == LED_FULL){ //用戶輸入255時亮 對應用戶輸入的是echo 255 > brightness writel(readl(GPJ0DAT) & ~(1 << 5), GPJ0DAT); //這樣操作保持其他位值不變 } } static int __init whyx210_led_init(void) { int ret = -1; //led1 //填充我們要註冊的struct led_classdev類型的結構體 myled1.name = "led1"; //將來sys/class/leds/目錄下的那個led文件的名字。leds這個目錄在led-class.c中內核幫我們實現好了、 myled1.brightness = 255; myled1.brightness_set = whyx210_led1_set; //去調用led驅動框架中為我們提供的led註冊函數led_classdev_register去註冊驅動 //在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) ret = led_classdev_register(NULL, &myled1); if (ret < 0) { printk(KERN_ERR "led_classdev_register errro\n"); return ret; } //printk(KERN_INFO "led_classdev_register success %s\n", myled.name); /*********************************************************************************************/ //led2 //填充我們要註冊的struct led_classdev類型的結構體 myled2.name = "led2"; //將來sys/class/leds/目錄下的那個led文件的名字。leds這個目錄在led-class.c中內核幫我們實現好了、 myled2.brightness = 255; myled2.brightness_set = whyx210_led2_set; //去調用led驅動框架中為我們提供的led註冊函數led_classdev_register去註冊驅動 //在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) ret = led_classdev_register(NULL, &myled2); if (ret < 0) { printk(KERN_ERR "led_classdev_register errro\n"); return ret; } //printk(KERN_INFO "led_classdev_register success %s\n", myled.name); /*********************************************************************************************/ //led3 //填充我們要註冊的struct led_classdev類型的結構體 myled3.name = "led3"; //將來sys/class/leds/目錄下的那個led文件的名字。leds這個目錄在led-class.c中內核幫我們實現好了、 myled3.brightness = 255; myled3.brightness_set = whyx210_led3_set; //去調用led驅動框架中為我們提供的led註冊函數led_classdev_register去註冊驅動 //在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) ret = led_classdev_register(NULL, &myled3); if (ret < 0) { printk(KERN_ERR "led_classdev_register errro\n"); return ret; } //printk(KERN_INFO "led_classdev_register success %s\n", myled.name); return 0; } static void __init whyx210_led_exit(void) { led_classdev_unregister(&myled1); led_classdev_unregister(&myled2); led_classdev_unregister(&myled3); //printk(KERN_INFO "led_classdev_unregister success.\n"); } module_init(whyx210_led_init); module_exit(whyx210_led_exit); MODULE_AUTHOR("why <[email protected]
現在先將led2和led3的代碼去掉,只留下led1,並且我們要知道我們怎麽去用platform平臺總線的方法去實現led的驅動,我們要有probe函數,和remove函數,分別對應的是驅動和設備匹配上後執行的probe函數,以及設備和驅動分離時的remove函數也就是卸載驅動的函數,我們要將數據部分的代碼寫入到platform的platform_data中,在驅動代碼的probe函數中,用參數的方式,將設備的數據部分得到,實現驅動和設備數據的代碼的分離邏輯框架。
上面的代碼只留下led1的驅動代碼後,代碼如下:
#include <linux/module.h> #include <linux/init.h> #include <linux/leds.h> #include <asm/io.h> //ioremap和iounmap的頭文件 writel等 /**********************************靜態映射虛擬地址的方法,用的是內核移植時的靜態映射表**********************************/ #include <mach/regs-gpio.h> #include <mach/gpio-bank.h> #define GPJ0CON S5PV210_GPJ0CON //這個宏是在gpio-bank.h中定義的,是虛擬地址,這個宏還用到了regs-gpio.h中的宏, //以此類推。 #define GPJ0DAT S5PV210_GPJ0DAT static struct led_classdev myled1; //這個函數綁定到了struct led_classdev這個結構體類型的myled變量中的brightness_set成員,當我們用戶寫這個led硬件的時候,因為是用led驅動框架 //寫的led驅動,所以我們是通過led驅動框架中的brightness這個文件,去寫或這個讀led硬件的,應為這個文件在驅動框架中具有了可讀可寫的屬性,是在 //struct //device_attribute結構中擁有的,並且最終綁定了到了這個設備類中。所以最後我們用戶去通過brightness這個屬性文件去寫led硬件的時候,會去執行led_br//ightness_store這個方法,這個方法中,又 //通過調用led_set_brightness這個函數,這個函數中通過led_cdev->brightness_set(led_cdev, value);這條語句,最終對應到了struct //led_classdev結構體中的brightness_set這個函數指針變量,而這個函數指針變量在我們填充那個結構體 //struct led_classdev中的brightness_set函數指針時,就已經綁定好了我們寫的那個寫操作硬件的函數了。所以這一條線就打通了.value就是用戶要寫的值 //value是枚舉,是enum led_brightness類型的枚舉,這個枚舉有三個值,分別是LED_OFF = 0 LED_XXX = 122 LED_FULL = 255, static void whyx210_led1_set(struct led_classdev *led_cdev, enum led_brightness value) { //printk(KERN_INFO "whyx210_led_set\n"); writel(0x11111111, GPJ0CON); if (value == LED_OFF) { //用戶輸入0時滅 對應用戶輸入的是echo 0 > brightness writel(readl(GPJ0DAT) | (1 << 3), GPJ0DAT); }else if (value == LED_FULL){ //用戶輸入255時亮 對應用戶輸入的是echo 255 > brightness writel(readl(GPJ0DAT) & ~(1 << 3), GPJ0DAT); //這樣操作保持其他位值不變 } } static int __init whyx210_led_init(void) { int ret = -1; //led1 //填充我們要註冊的struct led_classdev類型的結構體 myled1.name = "led1"; //將來sys/class/leds/目錄下的那個led文件的名字。leds這個目錄在led-class.c中內核幫我們實現好了、 myled1.brightness = 255; myled1.brightness_set = whyx210_led1_set; //去調用led驅動框架中為我們提供的led註冊函數led_classdev_register去註冊驅動 //在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) ret = led_classdev_register(NULL, &myled1); if (ret < 0) { printk(KERN_ERR "led_classdev_register errro\n"); return ret; } //printk(KERN_INFO "led_classdev_register success %s\n", myled.name); return 0; } static void __init whyx210_led_exit(void) { led_classdev_unregister(&myled1); } module_init(whyx210_led_init); module_exit(whyx210_led_exit); MODULE_AUTHOR("why <[email protected]