当前位置: 代码迷 >> 综合 >> Android8.1在misc设备上添加一个控制led的驱动
  详细解决方案

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

热度:32   发布时间:2023-12-15 14:16:21.0

在这里插入图片描述

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);elsereturn 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_valueen_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 申请占用该 GPIOret = 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_valuegpio_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_GPIObool "my_led gpio driver"default y   //y 表示选上, n 表示没有选上helpEnable 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.hGZIP   kernel/config_data.gzCHK    kernel/config_data.hUPD    kernel/config_data.hCC     kernel/configs.oLD     kernel/built-in.oCC     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是外接的一个小板
在这里插入图片描述
在这里插入图片描述

  相关解决方案