1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > Linux 设备树下的 platform 驱动实验基于正点原子IMX6ULL开发板

Linux 设备树下的 platform 驱动实验基于正点原子IMX6ULL开发板

时间:2023-01-17 18:26:38

相关推荐

Linux 设备树下的 platform 驱动实验基于正点原子IMX6ULL开发板

1 设备树下的 platform 驱动简介

platform 驱动框架分为总线、设备和驱动,其中总线不需要我们这些驱动程序员去管理,这个是 Linux 内核提供的,我们在编写驱动的时候只要关注于设备和驱动的具体实现即可。在没 有设备树的 Linux 内核下,我们需要分别编写并注册 platform_device 和 platform_driver,分别代表设备和驱动。**在使用设备树的时候,设备的描述被放到了设备树中,因此 platform_device 就不需要我们去编写了,我们只需要实现 platform_driver 即可。**在编写基于设备树的 platform 驱动的时候我们需要注意一下几点:

1、在设备树中创建设备节点

毫无疑问,肯定要先在设备树中创建设备节点来描述设备信息,重点是要设置好 compatible属性的值,因为 platform 总线需要通过设备节点的 compatible 属性值来匹配驱动!这点要切记。

比如,我们可以编写如下所示的设备节点来描述我们要用到的 LED 这个设备:

gpioled{compatible = "alientek,gpioled";pinctrl-names = "default";pinctrl-0 = <&pinctrl_gpioled>;led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;status= "okay";};

示例中 compatible 属性值为“alientek,gpioled”,因此一会在编写 platform驱动的时候 of_match_table 属性表中要有“alientek,gpioled”。

2、编写 platform 驱动的时候要注意兼容属性

在使用设备树的时候 platform 驱动会通过 of_match_table 来保存兼容性值,也就是表明此驱动兼容哪些设备。所以,of_match_table 将会尤为重要,比如本例程的 platform 驱动中 platform_driver 就可以按照如下所示设置:

1 static const struct of_device_id leds_of_match[] = {2 {.compatible = "alientek,gpioled" }, /* 兼容属性 */3 {/* Sentinel */ } 4 };5 6 MODULE_DEVICE_TABLE(of, leds_of_match);7 8 static struct platform_driver leds_platform_driver = {9 .driver = {10 .name = "imx6ull-led",/*无设备树和设备进行匹配,驱动名字*/11 .of_match_table = leds_of_match,/*设备树匹配表*/12 }, 13 .probe = leds_probe,14 .remove = leds_remove,15 };

第 1~4 行,of_device_id 表,也就是驱动的兼容表,是一个数组,每个数组元素为 of_device_id类型。每个数组元素都是一个兼容属性,表示兼容的设备,一个驱动可以跟多个设备匹配。这里我们仅仅匹配了一个设备,那就是1 中创建的 gpioled 这个设备。第 2 行的 compatible 值为“alientek,gpioled”,驱动中的 compatible 属性和设备中的 compatible 属性相匹配,因此驱动中对应的 probe 函数就会执行。注意第 3 行是一个空元素,在编写 of_device_id 的时候最后一个元素一定要为空!

第 6 行,通过 MODULE_DEVICE_TABLE 声明一下 leds_of_match 这个设备匹配表。

第 11 行,设置 platform_driver 中的 of_match_table 匹配表为上面创建的 leds_of_match,至

此我们就设置好了 platform 驱动的匹配表了。

3、编写 platform 驱动

基于设备树的 platform 驱动和无设备树的 platform 驱动基本一样,都是当驱动和设备匹配成功以后就会执行 probe 函数。我们需要在 probe 函数里面执行字符设备驱动那一套,当注销驱动模块的时候remove 函数就会执行,都是大同小异的。

2 硬件原理图分析

3 实验程序编写

我们编写基于设备树的 platform 驱动,所以需要在设备树中添加设备节点,然后我们只需要编写 platform 驱动即可。

3.1 修改设备树文件

首先修改设备树文件,加上我们需要的设备信息,本次实验我们就使用到一个 LED 灯,因此可以直接使用 以前实验编写的 gpioled 子节点即可,不需要再重复添加。

gpioled{compatible = "alientek,gpioled";pinctrl-names = "default";pinctrl-0 = <&pinctrl_gpioled>;led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;status= "okay";};

3.2 platform 驱动程序编写

leddriver.c 的驱动文件内容如下

#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/slab.h>#include <linux/uaccess.h>#include <linux/io.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/of.h>#include <linux/of_address.h>#include <linux/of_irq.h>#include <linux/gpio.h>#include <linux/of_gpio.h>#include <linux/string.h>#include <linux/irq.h>#include <asm/mach/map.h>#include <asm/uaccess.h>#include <asm/io.h>#include <linux/interrupt.h>#include <linux/poll.h>#include <linux/fcntl.h>#include <linux/ide.h>#include <linux/platform_device.h>#define GPIOLED_CNT 1#define GPIOLED_NAME "dtsplatled"#define LEDOFF 0#define LEDON 1/* gpioled设备结构体 */struct gpioled_dev{dev_t devid;int major;int minor;struct cdev cdev;struct class *class;struct device *device;struct device_node *nd;int led_gpio;};struct gpioled_dev gpioled; /* LED */static int led_open(struct inode *inode, struct file *filp){filp->private_data = &gpioled;return 0;}static int led_release(struct inode *inode, struct file *filp){return 0;}static ssize_t led_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos){int ret;unsigned char databuf[1];struct gpioled_dev *dev = filp->private_data;ret = copy_from_user(databuf, buf, count);if (ret < 0){return -EINVAL;}if (databuf[0] == LEDON){gpio_set_value(dev->led_gpio, 0);}else if (databuf[0] == LEDOFF){gpio_set_value(dev->led_gpio, 1);}return 0;}/* 操作集 */static const struct file_operations led_fops = {.owner = THIS_MODULE,.write = led_write,.open = led_open,.release = led_release,};static int led_probe(struct platform_device *dev){printk("led driver and device was matched!\r\n");int ret = 0;/* 注册字符设备驱动 */gpioled.major = 0;if (gpioled.major){/* 给定主设备号 */gpioled.devid = MKDEV(gpioled.major, 0);register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);}else{/* 没给定设备号 */alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);gpioled.major = MAJOR(gpioled.devid);gpioled.minor = MINOR(gpioled.devid);}printk("gpioled major = %d, minor = %d\r\n", gpioled.major, gpioled.minor);/* 2,初始化cdev */gpioled.cdev.owner = THIS_MODULE;cdev_init(&gpioled.cdev, &led_fops);/* 3,添加cdev */cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);/* 4、创建类 */gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);if (IS_ERR(gpioled.class)){return PTR_ERR(gpioled.class);}/* 5,创建设备 */gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);if (IS_ERR(gpioled.device)){return PTR_ERR(gpioled.device);}/* 1,获取设备节点 */#if 0gpioled.nd = of_find_node_by_path("/gpioled");if(gpioled.nd == NULL) {ret = -EINVAL;goto fail_findnode;}#endifgpioled.nd = dev->dev.of_node;/* 2, 获取LED所对应的GPIO */gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpios", 0);if (gpioled.led_gpio < 0){printk("can't find led gpio\r\n");ret = -EINVAL;goto fail_findnode;}printk("led gpio num = %d\r\n", gpioled.led_gpio);/* 3,申请IO */ret = gpio_request(gpioled.led_gpio, "led-gpio");if (ret){printk("Failed to request the led gpio\r\n");ret = -EINVAL;goto fail_findnode;}/* 4,使用IO,设置为输出 */ret = gpio_direction_output(gpioled.led_gpio, 1);if (ret){goto fail_setoutput;}/* 5,输出底电平,点亮LED灯*/gpio_set_value(gpioled.led_gpio, 0);return 0;fail_setoutput:gpio_free(gpioled.led_gpio);fail_findnode:return ret;}static int led_remove(struct platform_device *dev){printk("led remove\r\n");/* 关灯 */gpio_set_value(gpioled.led_gpio, 1);/* 注销字符设备驱动 */cdev_del(&gpioled.cdev);unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);device_destroy(gpioled.class, gpioled.devid);class_destroy(gpioled.class);/* 释放IO */gpio_free(gpioled.led_gpio);return 0;}/*匹配表,描述了此驱动都和什么样的设备匹配*/struct of_device_id led_of_match[] = {{.compatible = "alientek,gpioled"},{/*Sentinel*/},};struct platform_driver led_driver = {.driver = {.name = "imx6ull-led",/*无设备树和设备进行匹配,驱动名字*/.of_match_table = led_of_match, /*设备树匹配表*/},.probe = led_probe,.remove = led_remove,};/*驱动加载*/static int __init leddriver_init(void){return platform_driver_register(&led_driver);}/*驱动卸载*/static void __exit leddriver_exit(void){platform_driver_unregister(&led_driver);}module_init(leddriver_init);module_exit(leddriver_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("supersmart");

测试 APP platledApp.c内容如下

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string.h>/**argc:应用程序参数个数*argv[]:具体的参数内容,字符串形式 *./platledAPP <filename> <0:1> 0表示关灯,1表示开灯* ./platledAPP /dev/dtsplatled 0 关灯* ./platledAPP /dev/dtsplatled 1 开灯*/#define LEDOFF 0#define LEDON 1int main(int argc, char *argv[]){int fd, retvalue;char *filename;unsigned char databuf[1];if (argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);if (fd < 0){printf("file %s open failed!\r\n", filename);return -1;}databuf[0] = atoi(argv[2]); /* 将字符转换为数字 */retvalue = write(fd, databuf, sizeof(databuf));if (retvalue < 0){printf("LED Control Failed!\r\n");close(fd);return -1;}close(fd);return 0;}

4 运行测试

4.1 编译驱动程序和测试 APP

4.2 运行测试

将上一小节编译出来platledApp和 leddriver.ko 拷贝到 rootfs/lib/modules/4.1.15 目录中,进入到目录 lib/modules/4.1.15 中,输入如下命令加载 leddriver.ko 这个驱动模块。

我们在leddriver.c 中设置 led_driver (platform_driver 类型)的 name 字段为“imx6ull-led”,因此会在

/sys/bus/platform/drivers/目录下存在名为“imx6ull-led”这个文件

同理,在/sys/bus/platform/devices/目录下也存在 led 的设备文件,也就是设备树中 gpioled 这

个节点

驱动和设备匹配成功以后就可以测试 LED 灯驱动了,输入如下命令打开 LED 灯:

可以看到led灯的亮灭和预期一致。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。