Android8.1在misc设备上添加一个控制led的驱动

在这里插入图片描述

bash
**首先介绍一下注册一个驱动的步骤:**
1、定义一个platform_driver结构
2、初始化这个结构,指定其probe、remove等函数,
   并初始化其中的driver变量
3、实现其probe、remove等函数

在xxxxx.dts文件里面添加:
这里的"my_led_ctrl"很重要要和驱动的那边的相对应。

my_led {
	compatible = "my_led_ctrl";
	led_ctr0 = <&gpio1 RK_PB5 GPIO_ACTIVE_LOW>;
	//led_ctr1 = <&gpio1 RK_PB6 GPIO_ACTIVE_LOW>;
	//led_ctr2 = <&gpio1 RK_PB7 GPIO_ACTIVE_LOW>;
	status = "okay";
};

然后编写一个led控制的.c文件,一般不会自己啦,复制一份别的在做修改,我命名为my_gpio.c
在这里插入图片描述
my_gpio.c的内容:

/* SPDX-License-Identifier: GPL-2.0 */
#include <dt-bindings/gpio/gpio.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
#include <linux/slab.h>
static int My_led_ID;
static ssize_t gpio_store(struct device *dev, 
					struct device_attribute *attr, 
					const char *buf, size_t count) 
{
	unsigned long on = simple_strtoul(buf, NULL, 10);
	if(!strcmp(attr->attr.name, "My_led_value")){
		if(on){
			gpio_direction_output(My_led_ID, 1);
			printk("gpio_direction_output is %d \n",My_led_ID);
		}
		else{
			gpio_direction_output(My_led_ID, 0);
			printk("gpio_direction_output is 0\n");
		}
	}
	return count;
}
static ssize_t gpio_show(struct device *dev,
                 struct device_attribute *attr,
                 char *buf)
{
	int tmp = 0;
	if(!strcmp(attr->attr.name, "My_led_value")){
		tmp = gpio_get_value(My_led_ID);
		printk("gpio_get_value is %d \n",tmp);
		if(tmp>0)
			return strlcpy(buf, "1\n", 3);
		else
			return strlcpy(buf, "0\n", 3);
	}
	return 0;
}
/*
DEVICE_ATTR的使用  使用DEVICE_ATTR,可以在sys fs中添加“文件”,
通过修改该文件内容,可以实现在运行过程中动态控制device的目的。
类似的还有DRIVER_ATTR,BUS_ATTR,CLASS_ATTR。这几个东东的区别
就是,DEVICE_ATTR对应的文件在/sys/devices/目录中对应的device下
面。而其他几个分别在driver,bus,class中对应的目录下。这次主要
介绍DEVICE_ATTR,其他几个类似。在documentation/driver-model/Device.txt
中有对DEVICE_ATTR的详细介绍,这儿主要说明使用方法。  
先看看DEVICE_ATTR的原型:
DEVICE_ATTR(_name, _mode, _show, _store) _name:   名称,也就是将在sysfs中生成的文件名称。 
_mode:上述文件的访问权限,与普通文件相同,UGO的格式。 
_show:显示函数,cat该文件时,此函数被调用。 
_store:写函数,echo内容到该文件时,此函数被调用。
*/
// S_IRWXU | S_IRWXG 读写权限
static DEVICE_ATTR(My_led_value, S_IRWXU | S_IRWXG, gpio_show,gpio_store);
/*匹配dts文件中的compatible = "my_led_ctrl" */
static struct of_device_id My_led_of_match[] = {
	{ .compatible = "my_led_ctrl" },
	{ }
};
/*
MODULE_DEVICE_TABLE一般用在热插拔的设备驱动中。
作用是:模块加载系统再加载模块时,就知道了什么模块对应什么硬件设备。
用法是:MODULE_DEVICE_TABLE(设备类型,设备表)
设备类型:包括USB,PCI等,也可以自己起名字。
设备表:也是自己定义的,它的最后一项必须是空,用来标识结束。
*/
MODULE_DEVICE_TABLE(of, My_led_of_match);
static int My_led_probe(struct platform_device *pdev)
{
        struct device_node *node = pdev->dev.of_node;//获取平台设备节点
        enum of_gpio_flags flags;
        int ret;
        int en_value;
	    struct kobject * gpio_obj;
        if (!node)return -ENODEV; //如果设备节点不存在,则返回 -ENODEV(设备不存在)
		//从设备树中读取 my_led  GPIO 配置编号和标志
		//设备树: led_ctr0 = <&gpio1 RK_PB5 GPIO_ACTIVE_LOW>;
        My_led_ID = of_get_named_gpio_flags(node, "led_ctr0", 0, &flags);
		//标志获取完之后,做一个三目运算,将 1 或者 0 赋值给 en_value
        en_value = (flags == GPIO_ACTIVE_HIGH)? 1:0;
        //gpio_is_valid 判断该 GPIO 编号是否有效,IO是否合法
		if(!gpio_is_valid(My_led_ID)){
		dev_err(&pdev->dev, "invalid power gpio%d\n", My_led_ID);
		}
        //devm_gpio_request 申请占用该 GPIO
		ret = devm_gpio_request(&pdev->dev, My_led_ID, "my_gpio_led");
		if (ret) {
			dev_err(&pdev->dev,"failed to request GPIO%d for my_led \n",My_led_ID);
			return -EINVAL;//返回 -EINVAL (模式值无效)
		}
		//gpio_direction_output 设置输出高还是低电平,根据上面的 三目运算,将 1 或者 0 赋值给 en_value
		gpio_direction_output(My_led_ID, en_value);
        //create 一个节点
		gpio_obj = kobject_create_and_add("myled", NULL);
		//如果是空的,则返回错误
		if(gpio_obj == NULL){
		  printk("kobject_create_and_add error\n");
		  return -EINVAL;//返回 -EINVAL (模式值无效)
		}	
		//		
		ret = sysfs_create_file(gpio_obj,&dev_attr_My_led_value.attr);
		if (ret) {
		  printk("sysfs_create_file error\n");
		  return ret;
		}
		 return 0;
}
static int My_led_remove(struct platform_device *pdev)
{
	printk("My_led_remove");
	return 0;
}
#ifdef CONFIG_PM_SLEEP
static int My_led_suspend(struct device *dev)
{
	printk("My_led_suspend");
	return 0;
}
static int My_led_resume(struct device *dev)
{
	printk("My_led_resume");
	return 0;
}
#endif
static const struct dev_pm_ops My_led_pm_ops = {
#ifdef CONFIG_PM_SLEEP
	.suspend = My_led_suspend,
	.resume = My_led_resume,
	.poweroff = My_led_suspend,
	.restore = My_led_resume,
#endif
};
/*  定义一个平台设备  My_led_driver  */
static struct platform_driver My_led_driver = {
	.driver	= {
		.name		= "my_led_ctr0",
		.owner		= THIS_MODULE,
		.pm		= & My_led_pm_ops,
		.of_match_table	= of_match_ptr(My_led_of_match),
	},
	.probe		= My_led_probe,
	.remove		= My_led_remove,
};
module_platform_driver(My_led_driver);
MODULE_DESCRIPTION("power for My led gpio Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:_My_led_");

那么接下来是对Makefile还有Kconfig这两个文件的修改:
Makefile文件的修改添加:

obj-$(CONFIG_MY_GPIO)	+= my_gpio.o

对Kconfig`文件的修改添加:

config MY_GPIO
	bool "my_led gpio driver"
	default y   //y 表示选上, n 表示没有选上
	help
	  Enable this driver will support my_led control	  

在kernel目录下:make menuconfig

px30_android8.1/kernel$ make menuconfig

按步骤依次选择如下:
1、
在这里插入图片描述
2、
在这里插入图片描述
3、这里的 my_led gpio driver (NEW) 已经被选上。在这里插入图片描述
4、Yes 选择一下
在这里插入图片描述
编译kernel:

px30_android8.1/kernel$  make ARCH=arm64 px30-evb-ddr3-v10-rk618-lvds-1920x1080.img && cd ..   

在编译过程中我已经看到了(下面的最底部)

make[1]: 'include/generated/vdso-offsets.h' is up to date.
  CHK    include/generated/compile.h
  GZIP   kernel/config_data.gz
  CHK    kernel/config_data.h
  UPD    kernel/config_data.h
  CC     kernel/configs.o
  LD     kernel/built-in.o
  CC     drivers/misc/my_gpio.o //已经生成对应的my_gpio.o文件

使用ADB工具来调试:
echo 1 led亮,echo 0 led灭。

C:\Users\JLD\adb\platform-tools>adb shell
rk3326_m2g:/ $ su
rk3326_m2g:/ # echo 1 > sys/myled/My_led_value
rk3326_m2g:/ # echo 0 > sys/myled/My_led_value

对应的打印信息:
在这里插入图片描述

[  232.206147] gpio_direction_output is 45 
[  235.526100] gpio_direction_output is 0
[  638.359292] gpio_direction_output is 45 
[  649.008609] gpio_direction_output is 0

硬件原理图:

LED是外接的一个小板
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_38312843/article/details/103918952
今日推荐