Linux编写一个简单Linux驱动Demo -- 控制LED
Posted ZHONGCAI0901
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux编写一个简单Linux驱动Demo -- 控制LED相关的知识,希望对你有一定的参考价值。
1. 前言
本篇文章主要是介绍在linux平台上,如何编写一个简单的Driver来控制LED的亮灭。我们通过DeviceTree来定义硬件资源,和Driver匹配成功之后可以拿到硬件资源控制硬件GPIO。下面是APP、Driver和硬件的架构,如下:
2. 设备树和platform_driver的匹配
设备树节点是根据platform_driver的要求来编写的,匹配的过程有它固定的要求:
- 设备树要有
compatible
属性,它的值是一个字符串。 - platform_driver 中要有
of_match_table
,其中一项的.compatible
成员设置为一个字符串。 - 上述 2 个字符串要一致。
如下图所示:
DeviceTree能够和platform_driver匹配成功,其它经历了如下的过程:
3. 编写led driver
编写一个linux的driver一般需要以下几个步骤:
- 定义platform_driver
- 在入口函数注册platform_driver
- 卸载驱动程序时,就会调用出口函数:卸载platform_driver
- 匹配成功后,它会调用probe函数
具体的源码如下:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#ifdef CONFIG_OF
#include <linux/of_gpio.h>
#endif
static int led_ctrl_major = 0;
static struct class *led_ctrl_class;
static int led_gpio;
static int led_ctrl_open(struct inode *pInode, struct file *pFile)
{
printk("%s %s line %d\\n", __FILE__, __FUNCTION__, __LINE__);
gpio_direction_output(led_gpio, 1); // 设置GPIO的状态为output,并且初始化GPIO输出低高电平。
return 0;
}
static ssize_t led_ctrl_read(struct file *pFile, char __user *pBuf, size_t size, loff_t *ppos)
{
//int err;
printk("%s %s line %d\\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static ssize_t led_ctrl_write(struct file *pFile, const char __user *pBuf, size_t size, loff_t *ppos)
{
int err;
char status;
printk("%s %s line %d\\n", __FILE__, __FUNCTION__, __LINE__);
err = copy_from_user(&status, pBuf,1);
gpio_set_value(led_gpio, status); // 根据user的value来设置GPIO的状态
return 1;
}
static int led_ctrl_close(struct inode *pInode, struct file *pFile)
{
printk("%s %s line %d\\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* 定义自己的file_operations结构体 */
static const struct file_operations led_ctrl_fops = {
.owner = THIS_MODULE,
.open = led_ctrl_open,
.read = led_ctrl_read,
.write = led_ctrl_write,
.release = led_ctrl_close,
};
/* 4. 从platform_device获取GPIO,把file_operations结构体告诉内核:注册驱动程序 */
static int led_ctrl_probe(struct platform_device *ofdev)
{
int ret = 0;
struct device_node *np = ofdev->dev.of_node;
printk("%s %s line %d\\n", __FILE__, __FUNCTION__, __LINE__);
/* 4.1 设备树中定义有:led-gpios=<...> */
led_gpio = of_get_named_gpio(np, "led-gpios", 0);
if (!gpio_is_valid(led_gpio)){
dev_err(&ofdev->dev, "Fail to get GPIO for led\\n");
return -ENODEV;
}
if (gpio_is_valid(led_gpio)) {
ret = gpio_request(led_gpio, "led_ctrl");
if(ret < 0){
dev_err(&ofdev->dev, "Failed to request GPIO:%d, ERRNO:%d\\n", led_gpio, ret);
return -ENODEV;
}
gpio_direction_output(led_gpio, 1);
}
/* 4.2 注册file_operations */
led_ctrl_major = register_chrdev(0, "led_ctrl", &led_ctrl_fops);
led_ctrl_class = class_create(THIS_MODULE, "led_ctrl_class");
if (IS_ERR(led_ctrl_class)) {
printk("%s %s line %d\\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(led_ctrl_major, "led_ctrl");
gpio_free(led_gpio);
return PTR_ERR(led_ctrl_class);
}
/* 4.3 创建一个设备/dev/led0,并且注册到文件系统。 */
device_create(led_ctrl_class, NULL, MKDEV(led_ctrl_major, 0), NULL, "led%d", 0); /* /dev/led0 */
return 0;
}
static int led_ctrl_remove(struct platform_device *ofdev)
{
return 0;
}
static const struct of_device_id led_ctrl_match[] = {
{ .compatible = "imx,led_ctrl" },
{},
};
/* 1. 定义platform_driver*/
static struct platform_driver led_ctrl_driver = {
.driver = {
.name = "led_ctrl",
.of_match_table = led_ctrl_match,
},
.probe = led_ctrl_probe,
.remove = led_ctrl_remove,
};
/* 2. 在入口函数注册platform_driver */
static int __init led_ctrl_init(void)
{
platform_driver_register(&led_ctrl_driver);
return 0;
}
/* 3. 卸载驱动程序时,就会条用这个出口函数:卸载platform_driver */
static void __exit led_ctrl_exit(void)
{
platform_driver_unregister(&led_ctrl_driver);
}
module_init(led_ctrl_init);
module_exit(led_ctrl_exit);
MODULE_LICENSE("GPL");
4. 编写led app
我们通过编写一个app来操作led driver来控制LED的亮灭,具体的源码如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
/*
* ./led_ctrl_app /dev/led0 on
* ./led_ctrl_app /dev/led0 off
*/
int main(int argc, char **argv)
{
int fd;
char status;
/* 1.判断参数 */
if(argc != 3)
{
printf("Usage: %s <dev> <on | off>\\n", argv[0]);
return -1;
}
/* 2.打开文件 */
fd = open(argv[1], O_RDWR);
if(fd == -1)
{
printf("can not open file %s\\n", argv[1]);
return -1;
}
/* 3.写文件 */
if(0 == strcmp(argv[2], "on"))
{
status = 0;
write(fd, &status, 1);
}
else
{
status = 1;
write(fd, &status, 1);
}
/* 4.关闭文件 */
close(fd);
return 0;
}
5. 测试验证
首先我需要加载driver,然后运行测试程序:
# insmod led_ctrl.ko
#
# ./led_ctrl_app /dev/led0 on
# ./led_ctrl_app /dev/led0 off
运行的命令的结果如下:
运行效果如下:
6. 测试demo完整工程
测试时的完成工程代码下载路径如下:
https://download.csdn.net/download/ZHONGCAI0901/21439383
以上是关于Linux编写一个简单Linux驱动Demo -- 控制LED的主要内容,如果未能解决你的问题,请参考以下文章
Linux虚拟Pinctrl Demo驱动-- Demo Code