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的要求来编写的,匹配的过程有它固定的要求:

  1. 设备树要有 compatible 属性,它的值是一个字符串。
  2. platform_driver 中要有 of_match_table,其中一项的.compatible 成员设置为一个字符串。
  3. 上述 2 个字符串要一致。

如下图所示:

DeviceTree能够和platform_driver匹配成功,其它经历了如下的过程:

3. 编写led driver

编写一个linux的driver一般需要以下几个步骤:

  1. 定义platform_driver
  2. 在入口函数注册platform_driver
  3. 卸载驱动程序时,就会调用出口函数:卸载platform_driver
  4. 匹配成功后,它会调用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

Linux虚拟Pinctrl Demo驱动-- Demo Code

Linux编写一个Linux按键中断Demo

Linux编写一个Linux按键中断Demo

Linux虚拟Pinctrl Demo驱动

实战!手把手教你如何编写一个Linux驱动并写一个支持物联网的LED演示demo