大庆免费网站建设公司,织梦图片自适应网站源码,河南省建设工程招标投标协会网站,杭州网络公司目录设备树下的platform 驱动简介硬件原理图分析实验程序编写修改设备树文件platform 驱动程序编写编写测试APP运行测试编译驱动程序和测试APP运行测试上一章我们详细的讲解了Linux 下的驱动分离与分层#xff0c;以及总线、设备和驱动这样的驱动框架。基于总线、设备和驱动这…
目录设备树下的platform 驱动简介硬件原理图分析实验程序编写修改设备树文件platform 驱动程序编写编写测试APP运行测试编译驱动程序和测试APP运行测试上一章我们详细的讲解了Linux 下的驱动分离与分层以及总线、设备和驱动这样的驱动框架。基于总线、设备和驱动这样的驱动框架Linux 内核提出来platform 这个虚拟总线相应的也有platform 设备和platform 驱动。上一章我们讲解了传统的、未采用设备树的platform 设备和驱动编写方法。最新的Linux 内核已经支持了设备树因此在设备树下如何编写platform驱动就显得尤为重要本章我们就来学习一下如何在设备树下编写platform 驱动。
设备树下的platform 驱动简介
platform 驱动框架分为总线、设备和驱动其中总线不需要我们这些驱动程序员去管理这个是Linux 内核提供的我们在编写驱动的时候只要关注于设备和驱动的具体实现即可。在没有设备树的Linux 内核下我们需要分别编写并注册platform_device 和platform_driver分别代表设备和驱动。在使用设备树的时候设备的描述被放到了设备树中因此platform_device 就不需要我们去编写了我们只需要实现platform_driver 即可。在编写基于设备树的platform 驱动的时候我们需要注意一下几点
1、在设备树中创建设备节点 毫无疑问肯定要先在设备树中创建设备节点来描述设备信息重点是要设置好compatible属性的值因为platform 总线需要通过设备节点的compatible 属性值来匹配驱动这点要切记。 比如我们可以编写如下所示的设备节点来描述我们本章实验要用到的LED 这个设备
1 gpioled {
2 #address-cells 1;
3 #size-cells 1;
4 compatible atkalpha-gpioled;
5 pinctrl-names default;
6 pinctrl-0 pinctrl_led;
7 led-gpio gpio1 3 GPIO_ACTIVE_LOW;
8 status okay;
9 };示例55.1.1 中的gpioled 节点其实就是45.4.1.2 小节中创建的gpioled 设备节点我们可以直接拿过来用。注意第4 行的compatible 属性值为“atkalpha-gpioled”因此一会在编写platform驱动的时候of_match_table 属性表中要有“atkalpha-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 atkalpha-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 imx6ul-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类型。每个数组元素都是一个兼容属性表示兼容的设备一个驱动可以跟多个设备匹配。这里我们仅仅匹配了一个设备那就是55.1.1 中创建的gpioled 这个设备。第2 行的compatible 值为“atkalpha-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 函数就会执行都是大同小异的。
硬件原理图分析
本章实验我们只使用到IMX6U-ALPHA 开发板上的LED 灯因此实验硬件原理图参考8.3小节即可。
实验程序编写
本实验对应的例程路径为开发板光盘- 2、Linux 驱动例程- 18_dtsplatform。 本章实验我们编写基于设备树的platform 驱动所以需要在设备树中添加设备节点然后我们只需要编写platform 驱动即可。
修改设备树文件
首先修改设备树文件加上我们需要的设备信息本章我们就使用到一个LED 灯因此可以直接使用45.4.1 小节编写的gpioled 子节点即可不需要再重复添加。
platform 驱动程序编写
设备已经准备好了接下来就要编写相应的platform 驱动了新建名为“18_dtsplatform”的文件夹然后在18_dtsplatform 文件夹里面创建vscode 工程工作区命名为“dtsplatform”。 新建名为leddriver.c 的驱动文件在leddriver.c 中输入如下所示内容
#include linux/types.h
#include linux/kernel.h
#include linux/delay.h
#include linux/ide.h
#include linux/init.h
#include linux/module.h
#include linux/errno.h
#include linux/gpio.h
#include linux/cdev.h
#include linux/device.h
#include linux/of_gpio.h
#include linux/semaphore.h
#include linux/timer.h
#include linux/irq.h
#include linux/wait.h
#include linux/poll.h
#include linux/fs.h
#include linux/fcntl.h
#include linux/platform_device.h
#include asm/mach/map.h
#include asm/uaccess.h
#include asm/io.h
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : leddriver.c
作者 : 左忠凯
版本 : V1.0
描述 : 设备树下的platform驱动
其他 : 无
论坛 : www.openedv.com
日志 : 初版V1.0 2019/8/13 左忠凯创建
***************************************************************/#define LEDDEV_CNT 1 /* 设备号长度 */
#define LEDDEV_NAME dtsplatled /* 设备名字 */
#define LEDOFF 0
#define LEDON 1/* leddev设备结构体 */
struct leddev_dev{dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */int major; /* 主设备号 */ struct device_node *node; /* LED设备节点 */int led0; /* LED灯GPIO标号 */
};struct leddev_dev leddev; /* led设备 *//** description : LED打开/关闭* param - sta : LEDON(0) 打开LEDLEDOFF(1) 关闭LED* return : 无*/
void led0_switch(u8 sta)
{if (sta LEDON )gpio_set_value(leddev.led0, 0);else if (sta LEDOFF)gpio_set_value(leddev.led0, 1);
}/** description : 打开设备* param - inode : 传递给驱动的inode* param - filp : 设备文件file结构体有个叫做private_data的成员变量* 一般在open的时候将private_data指向设备结构体。* return : 0 成功;其他 失败*/
static int led_open(struct inode *inode, struct file *filp)
{filp-private_data leddev; /* 设置私有数据 */return 0;
}/** description : 向设备写数据 * param - filp : 设备文件表示打开的文件描述符* param - buf : 要写给设备写入的数据* param - cnt : 要写入的数据长度* param - offt : 相对于文件首地址的偏移* return : 写入的字节数如果为负值表示写入失败*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{int retvalue;unsigned char databuf[2];unsigned char ledstat;retvalue copy_from_user(databuf, buf, cnt);if(retvalue 0) {printk(kernel write failed!\r\n);return -EFAULT;}ledstat databuf[0];if (ledstat LEDON) {led0_switch(LEDON);} else if (ledstat LEDOFF) {led0_switch(LEDOFF);}return 0;
}/* 设备操作函数 */
static struct file_operations led_fops {.owner THIS_MODULE,.open led_open,.write led_write,
};/** description : flatform驱动的probe函数当驱动与* 设备匹配以后此函数就会执行* param - dev : platform设备* return : 0成功;其他负值,失败*/
static int led_probe(struct platform_device *dev)
{ printk(led driver and device was matched!\r\n);/* 1、设置设备号 */if (leddev.major) {leddev.devid MKDEV(leddev.major, 0);register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);} else {alloc_chrdev_region(leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);leddev.major MAJOR(leddev.devid);}/* 2、注册设备 */cdev_init(leddev.cdev, led_fops);cdev_add(leddev.cdev, leddev.devid, LEDDEV_CNT);/* 3、创建类 */leddev.class class_create(THIS_MODULE, LEDDEV_NAME);if (IS_ERR(leddev.class)) {return PTR_ERR(leddev.class);}/* 4、创建设备 */leddev.device device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);if (IS_ERR(leddev.device)) {return PTR_ERR(leddev.device);}/* 5、初始化IO */ leddev.node of_find_node_by_path(/gpioled);if (leddev.node NULL){printk(gpioled node nost find!\r\n);return -EINVAL;} leddev.led0 of_get_named_gpio(leddev.node, led-gpio, 0);if (leddev.led0 0) {printk(cant get led-gpio\r\n);return -EINVAL;}gpio_request(leddev.led0, led0);gpio_direction_output(leddev.led0, 1); /* led0 IO设置为输出默认高电平 */return 0;
}/** description : platform驱动的remove函数移除platform驱动的时候此函数会执行* param - dev : platform设备* return : 0成功;其他负值,失败*/
static int led_remove(struct platform_device *dev)
{gpio_set_value(leddev.led0, 1); /* 卸载驱动的时候关闭LED */cdev_del(leddev.cdev); /* 删除cdev */unregister_chrdev_region(leddev.devid, LEDDEV_CNT); /* 注销设备号 */device_destroy(leddev.class, leddev.devid);class_destroy(leddev.class);return 0;
}/* 匹配列表 */
static const struct of_device_id led_of_match[] {{ .compatible atkalpha-gpioled },{ /* Sentinel */ }
};/* platform驱动结构体 */
static struct platform_driver led_driver {.driver {.name imx6ul-led, /* 驱动名字用于和设备匹配 */.of_match_table led_of_match, /* 设备树匹配表 */},.probe led_probe,.remove led_remove,
};/** description : 驱动模块加载函数* param : 无* return : 无*/
static int __init leddriver_init(void)
{return platform_driver_register(led_driver);
}/** description : 驱动模块卸载函数* param : 无* return : 无*/
static void __exit leddriver_exit(void)
{platform_driver_unregister(led_driver);
}module_init(leddriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE(GPL);
MODULE_AUTHOR(zuozhongkai);第33~112 行传统的字符设备驱动没什么要说的。
第120~164 行platform 驱动的probe 函数当设备树中的设备节点与驱动之间匹配成功以后此函数就会执行原来在驱动加载函数里面做的工作现在全部放到probe 函数里面完成。
第171~180 行remobe 函数当卸载platform 驱动的时候此函数就会执行。在此函数里面释放内存、注销字符设备等也就是将原来驱动卸载函数里面的工作全部都放到remove 函数中完成。
第183~186 行匹配表描述了此驱动都和什么样的设备匹配第184 行添加了一条值为atkalpha-gpioled的compatible 属性值当设备树中某个设备节点的compatible 属性值也为“atkalpha-gpioled”的时候就会与此驱动匹配。
第189~196 行platform_driver 驱动结构体191 行设置这个platform 驱动的名字为“imx6ul-led”因此当驱动加载成功以后就会在/sys/bus/platform/drivers/目录下存在一个名为“imx6u- led”的文件。第192 行设置of_match_table 为上面的led_of_match。
第203~206 行驱动模块加载函数在此函数里面通过platform_driver_register 向Linux 内核注册led_driver 驱动。
第213~216 行驱动模块卸载函数在此函数里面通过platform_driver_unregister 从Linux内核卸载led_driver 驱动。
编写测试APP
测试APP 就直接使用上一章54.4.2 小节编写的ledApp.c 即可。
运行测试
编译驱动程序和测试APP
1、编译驱动程序 编写Makefile 文件本章实验的Makefile 文件和第四十章实验基本一样只是将obj-m 变量的值改为“leddriver.o”Makefile 内容如下所示
KERNELDIR : /home/zuozhongkai/linux/IMX6ULL/linux/temp/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH : $(shell pwd)obj-m : leddriver.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M$(CURRENT_PATH) modulesclean:$(MAKE) -C $(KERNELDIR) M$(CURRENT_PATH) clean第4 行设置obj-m 变量的值为“leddriver.o”。 输入如下命令编译出驱动模块文件
make -j32编译成功以后就会生成一个名为“leddriver.o”的驱动模块文件。 2、编译测试APP
测试APP 直接使用上一章的ledApp 这个测试软件即可。
运行测试
将上一小节编译出来leddriver.ko 拷贝到rootfs/lib/modules/4.1.15 目录中重启开发板进入到目录lib/modules/4.1.15 中输入如下命令加载leddriver.ko 这个驱动模块。
depmod //第一次加载驱动的时候需要运行此命令
modprobe leddriver.ko //加载驱动模块驱动模块加载完成以后到/sys/bus/platform/drivers/目录下查看驱动是否存在我们在leddriver.c 中设置led_driver (platform_driver 类型)的name 字段为“imx6ul-led”因此会在/sys/bus/platform/drivers/目录下存在名为“imx6ul-led”这个文件结果如图55.4.2.1 所示 同理在/sys/bus/platform/devices/目录下也存在led 的设备文件也就是设备树中gpioled 这个节点如图55.4.2.2 所示 驱动和模块都存在当驱动和设备匹配成功以后就会输出如图55.4.2.3 所示一行语句
驱动和设备匹配成功以后就可以测试LED 灯驱动了输入如下命令打开LED 灯
./ledApp /dev/dtsplatled 1 //打开LED 灯在输入如下命令关闭LED 灯
./ledApp /dev/dtsplatled 0 //关闭LED 灯观察一下LED 灯能否打开和关闭如果可以的话就说明驱动工作正常如果要卸载驱动的话输入如下命令即可
rmmod leddriver.ko