例程代碼路徑:ELF 1開發(fā)板資料包3-例程源碼3-2 驅(qū)動例程源碼6_platformplatform_led
下面以控制開發(fā)板上的LED_R為例進行講解。
修改設(shè)備樹
(一)查看原理圖和引腳復用表格,可以得到LED_R由GPIO1_10控制,所以我們需要配置GPIO1_10引腳為輸出,而且能夠在用戶空間控制它輸出高電平還是低電平。
(二)在設(shè)備樹arch/arm/boot/dts/imx6ull-elf1-emmc.dts中添加leds節(jié)點和引腳復用,并檢查設(shè)備樹中是否有其它的地方也用到了此引腳,如果用到了就需要將其屏蔽掉,避免復用沖突。
添加節(jié)點:
leds { ????????????????compatible = "platform_led"; ????????????????pinctrl-names = "default"; ????????????????pinctrl-0 = <&pinctrl_led>; ????????????????gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; ????????}; |
添加后效果如下:
添加復用,在iomux中新建pinctrl_led:
pinctrl_led:ledgrp { ????????????????????????fsl,pins = < ????????????????????????????????MX6UL_PAD_JTAG_MOD__GPIO1_IO10 ???????0x10b0 ????????????????????????>; ????????????????}; |
添加后效果如下:
(三)編譯設(shè)備樹:
. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi elf@ubuntu:~/work/linux-imx-imx_4.1.15_2.0.0_ga$ make dtbs |
編譯生成的設(shè)備樹文件為imx6ull-elf1-emmc.dtb,參考《01-0 ELF1、ELF1S開發(fā)板_快速啟動手冊_V1》4.4節(jié)單獨更新設(shè)備樹。
驅(qū)動源碼platform_led.c編寫
(一)頭文件引用
#include <linux/init.h> #include <linux/module.h> ??????// 包含模塊相關(guān)函數(shù)的頭文件 #include <linux/fs.h> ??????????// 包含文件系統(tǒng)相關(guān)函數(shù)的頭文件 #include <linux/platform_device.h> #include <linux/miscdevice.h> #include <linux/device.h> ??????//包含設(shè)備節(jié)點相關(guān)的頭文件 #include <linux/gpio.h> ????????//包含gpio操作函數(shù)的相關(guān)頭文件 #include <linux/of_gpio.h> #include <linux/of.h> |
(二)創(chuàng)建相關(guān)宏定義
#define DEVICE_NAME "my_device" #define LED_IOC_MAGIC 'k' #define SET_LED_ON _IO(LED_IOC_MAGIC, 0) #define SET_LED_OFF _IO(LED_IOC_MAGIC, 1) ? struct device_node *node; ??//設(shè)備樹節(jié)點 int gpio; ??//gpio編號 |
這里同樣使用ioctl來控制,ioctl相關(guān)的定義本節(jié)不在重復講解。
(三)定義platform_driver類型結(jié)構(gòu)體
static struct platform_driver my_platform_driver = { ????.driver = { ????????.name = "my_platform_driver", ????????.owner = THIS_MODULE, ????????.of_match_table = of_platform_match, ????}, ????.probe = my_platform_probe, ????.remove = my_platform_remove, }; |
(四)定義of_platform_match,用來與設(shè)備樹中的compatible匹配,匹配成功后才會進入到probe函數(shù)中
static const struct of_device_id of_platform_match[] = { ????????{ .compatible = "platform_led", }, ????????{}, }; |
(五)probe函數(shù)的實現(xiàn)
static int my_platform_probe(struct platform_device *pdev) { ????????struct device *dev = &pdev->dev; ????????int ret; ????????//注冊雜項設(shè)備 ????????ret = misc_register(&my_misc_device); ????????if (ret) { ????????pr_err("Failed to register misc devicen"); ????????return ret; ????????} ????????// 獲取設(shè)備節(jié)點 ????????node = of_find_node_by_name(NULL,"leds"); ????????// 獲取 gpios 屬性中的引腳編號 ????????gpio = of_get_named_gpio(node, "gpios", 0); ????????//判斷是否獲取成功 ?????????if (!gpio_is_valid(gpio)) { ?????????pr_err("Failed to get GPIOn"); ?????????return -1; ????????} ? ????????gpio_free(gpio); ????????if (gpio_request(gpio, "led_run")) { ????????????????printk("request %s gpio faile n", "led_run"); ?????????????????return -1; ?????????} ????????printk(KERN_INFO "my_platform_probe: Platform device probedn"); ????return 0; } |
(1)misc_register()用戶注冊雜項設(shè)備,原型如下:
int misc_register(struct miscdevice *misc); |
參數(shù)說明:
misc:指向struct miscdevice結(jié)構(gòu)體的指針,表示要注冊的雜項設(shè)備。
返回值是一個整數(shù),表示注冊結(jié)果。如果注冊成功,函數(shù)會返回0;如果注冊失敗,函數(shù)會返回一個負數(shù),表示錯誤代碼。
struct miscdevice結(jié)構(gòu)體用于描述雜項設(shè)備的屬性,包括設(shè)備的次設(shè)備號(minor number)、設(shè)備名稱、設(shè)備文件操作函數(shù)等。在注冊雜項設(shè)備之前,需要先初始化struct miscdevice 結(jié)構(gòu)體的字段,然后將其作為參數(shù)傳遞給 misc_register()函數(shù)。
(2)of_find_node_by_name()用于查找設(shè)備樹中的節(jié)點,原型如下:
struct device_node *of_find_node_by_name(struct device_node *prev, const char *name); |
參數(shù)說明:
prev:指向前一個設(shè)備樹節(jié)點的指針。如果要從整個設(shè)備樹中開始查找節(jié)點,則可以傳遞NULL。
name:字符串,表示要查找的設(shè)備樹節(jié)點的名稱。
返回值是一個指向找到的設(shè)備樹節(jié)點的指針。如果找到匹配的節(jié)點,則返回該節(jié)點的指針;如果未找到匹配的節(jié)點,則返回 NULL。
(3)of_get_named_gpio()用于從設(shè)備樹中獲取GPIO引腳編號,原型如下:
int of_get_named_gpio(const struct device_node *np, const char *propname, int index); |
參數(shù)說明:
np:指向設(shè)備樹節(jié)點的指針,表示要從該節(jié)點獲取 GPIO 引腳。
propname:字符串,表示要獲取的 GPIO 引腳屬性的名稱。
index:整數(shù),表示在屬性中指定的 GPIO 引腳的索引。如果屬性中有多個 GPIO 引腳定義,可以通過指定索引來獲取其中的一個引腳。
返回值是一個整數(shù),表示獲取到的 GPIO 引腳編號。如果獲取失敗,函數(shù)會返回一個負數(shù)。
(六)miscdevice類型結(jié)構(gòu)體定義
static struct miscdevice my_misc_device = { ????.minor = MISC_DYNAMIC_MINOR, ??// 動態(tài)分配次設(shè)備號 ????.name = DEVICE_NAME, ??????????// 設(shè)備名稱 ????.fops = &my_device_fops, ?????????// 設(shè)備文件操作函數(shù) }; |
(七)文件操作函數(shù),file_operations類型結(jié)構(gòu)體定義
static struct file_operations my_device_fops = { ????.owner = THIS_MODULE, ????.open = my_device_open, ????.release = my_device_release, ????.unlocked_ioctl = myled_ioctl, }; |
(八)文件操作函數(shù)實現(xiàn):
static int my_device_open(struct inode *inode, struct file *file) { // 打開設(shè)備的操作 //將引腳配置為輸出 ????gpio_direction_output(gpio, 1); ????printk(KERN_INFO "This is device_open.n"); ????????return 0; } ? static long myled_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { ? ????switch (cmd) { ????????????case SET_LED_ON: ????????????????printk(KERN_INFO "This is set_led_on.n"); ????????????// 設(shè)置GPIO引腳為低電平 ????????????gpio_set_value(gpio, 0); ????????????break; ? ????????case SET_LED_OFF: ??????????//設(shè)置GPIO引腳為高電平 ??????????printk(KERN_INFO "This is set_led_off.n"); ??????????gpio_set_value(gpio, 1); ????????????break; ? ????????default: ????????????return -ENOTTY; ????} ? ????return 0; } ? static int my_device_release(struct inode *inode, struct file *file) { ????// 關(guān)閉設(shè)備的操作 ????return 0; } |
文件操作函數(shù)同樣使用ioctl來實現(xiàn),與之前的LED操作一樣,接收到SET_LED_ON命令后將引腳拉低,接收到SET_LED_OFF命令后,將引腳電平拉高。
完整的驅(qū)動platform_led.c示例源碼
#include <linux/init.h> #include <linux/module.h> ??????// 包含模塊相關(guān)函數(shù)的頭文件 #include <linux/fs.h> ??????????// 包含文件系統(tǒng)相關(guān)函數(shù)的頭文件 #include <linux/platform_device.h> #include <linux/miscdevice.h> #include <linux/device.h> ??????//包含設(shè)備節(jié)點相關(guān)的頭文件 #include <linux/gpio.h> ????????//包含gpio操作函數(shù)的相關(guān)頭文件 #include <linux/of_gpio.h> #include <linux/of.h> ? #define DEVICE_NAME "my_device" #define LED_IOC_MAGIC 'k' #define SET_LED_ON _IO(LED_IOC_MAGIC, 0) #define SET_LED_OFF _IO(LED_IOC_MAGIC, 1) ? struct device_node *node; ??//設(shè)備樹節(jié)點 int gpio; ??//gpio編號 ? static int my_device_open(struct inode *inode, struct file *file) { // 打開設(shè)備的操作 //將引腳配置為輸出 ????gpio_direction_output(gpio, 1); ????printk(KERN_INFO "This is device_open.n"); ????????return 0; } ? static long myled_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { ? ????switch (cmd) { ????????????case SET_LED_ON: ????????????????printk(KERN_INFO "This is set_led_on.n"); ????????????// 設(shè)置GPIO引腳為低電平 ????????????gpio_set_value(gpio, 0); ????????????break; ? ????????case SET_LED_OFF: ??????????//設(shè)置GPIO引腳為高電平 ??????????printk(KERN_INFO "This is set_led_off.n"); ??????????gpio_set_value(gpio, 1); ????????????break; ? ????????default: ????????????return -ENOTTY; ????} ? ????return 0; } ? static int my_device_release(struct inode *inode, struct file *file) { ????// 關(guān)閉設(shè)備的操作 ????return 0; } ? static struct file_operations my_device_fops = { ????.owner = THIS_MODULE, ????.open = my_device_open, ????.release = my_device_release, ????.unlocked_ioctl = myled_ioctl, }; ? static struct miscdevice my_misc_device = { ????.minor = MISC_DYNAMIC_MINOR, ????.name = DEVICE_NAME, ????.fops = &my_device_fops, }; ? static int my_platform_probe(struct platform_device *pdev) { ????????struct device *dev = &pdev->dev; ????????int ret; ????????//注冊雜項設(shè)備 ????????ret = misc_register(&my_misc_device); ????????if (ret) { ????????pr_err("Failed to register misc devicen"); ????????return ret; ????????} ????????// 獲取設(shè)備節(jié)點 ????????node = of_find_node_by_name(NULL,"leds"); ????????// 獲取 gpios 屬性中的引腳編號 ????????gpio = of_get_named_gpio(node, "gpios", 0); ????????//判斷是否獲取成功 ?????????if (!gpio_is_valid(gpio)) { ?????????pr_err("Failed to get GPIOn"); ?????????return -1; ????????} ? ????????gpio_free(gpio); ????????if (gpio_request(gpio, "led_run")) { ????????????????printk("request %s gpio faile n", "led_run"); ?????????????????return -1; ?????????} ????????printk(KERN_INFO "my_platform_probe: Platform device probedn"); ????return 0; } static int my_platform_remove(struct platform_device *pdev) { ????????misc_deregister(&my_misc_device); ? ????????printk(KERN_INFO "my_platform_remove: Platform device removedn"); ????return 0; } ? static const struct of_device_id of_platform_match[] = { ????????{ .compatible = "platform_led", }, ????????{}, }; ? static struct platform_driver my_platform_driver = { ????.driver = { ????????.name = "my_platform_driver", ????????.owner = THIS_MODULE, ????????.of_match_table = of_platform_match, ????}, ????.probe = my_platform_probe, ????.remove = my_platform_remove, }; ? module_platform_driver(my_platform_driver); ? MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Platform Driver Example"); |
編譯
復制7.7.2驅(qū)動中的Makefile文件,將其中的platform.o修改為platform_led.o,效果如下:
. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi elf@ubuntu:~/work/test/06_platform/platform_led$?make ????????????????????????? |
將編譯生成的platform_led.ko模塊拷貝到開發(fā)板。
測試
測試app使用之前章節(jié)的app即可。
root@ELF1:~# insmod platform_led.ko my_platform_probe: Platform device probed root@ELF1:~# ./led_app This is device_open. This is set_led_on. This is set_led_off. This is set_led_on. This is set_led_off. This is set_led_on. This is set_led_off. This is set_led_on. This is set_led_off. This is set_led_on. This is set_led_off. root@ELF1:~# rmmod platform_led.ko my_platform_remove: Platform device removed |
此時可以看到開發(fā)板上的紅色LED,循環(huán)閃爍5次。