[linux 驱动]增加一个文件节点控制led灯亮灭

目录

1 修改设备树

2 修改驱动

3 驱动源码

3.1 驱动源码

3.2 设备树节点

3.3 驱动源码分析

3.3.1 ##解释

3.3.2 class_create解释

3.3.3 class_create_file解释

3.3.4 of_get_named_gpio_flags解释

3.3.5 devm_gpio_request解释

3.3.6 platform_driver_register解释

3.3.7 platform_driver_unregister解释


1 修改设备树

        修改设备树添加my_wifi_led资源。

--- a/kernel/linux-5.10/arch/arm64/boot/dts/rockchip/rk3588-ir88mx01-v10.dtsi
+++ b/kernel/linux-5.10/arch/arm64/boot/dts/rockchip/rk3588-ir88mx01-v10.dtsi
@@ -232,6 +232,8 @@
         gpio2_out = <&gpio3 RK_PA2 GPIO_ACTIVE_LOW>;
         gpio3_in = <&gpio3 RK_PA7 GPIO_ACTIVE_LOW>;
         gpio3_out = <&gpio3 RK_PA3 GPIO_ACTIVE_LOW>;
+
+       my_wifi_led = <&gpio4 RK_PC2 GPIO_ACTIVE_LOW>;
     };
 
     minix_modem {

        这样就可以在/proc/device-tree/jw_io_init/看到有my_wifi_led文件。

rockchip@ubuntu:/sys/class/jw_io_control$ ls /proc/device-tree/jw_io_init/
compatible  gpio0_in  gpio0_out  gpio1_in  gpio1_out  gpio2_in  gpio2_out  gpio3_in  gpio3_out  my_wifi_led  name  pinctrl-0  pinctrl-names  wifi_led  z32_reset  zigbee_led

2 修改驱动

        修改驱动添加能控制led灯亮灭的文件节点。

--- a/kernel/linux-5.10/drivers/jw/jw_gpio/jw_io_core.c
+++ b/kernel/linux-5.10/drivers/jw/jw_gpio/jw_io_core.c
@@ -111,7 +111,8 @@ CLASS_GPIO_DEFINE(gpio2_in, gpio2_in_pin)
 CLASS_GPIO_DEFINE(gpio2_out, gpio2_out_pin)
 CLASS_GPIO_DEFINE(gpio3_in, gpio3_in_pin)
 CLASS_GPIO_DEFINE(gpio3_out, gpio3_out_pin)
-
+CLASS_GPIO_DEFINE(my_wifi_led, wifi_led_pin)
+       
 static int jw_io_control_probe(struct platform_device *pdev)
 {
        struct device_node *node = pdev->dev.of_node;
@@ -141,6 +142,7 @@ static int jw_io_control_probe(struct platform_device *pdev)
     GPIO_REQUEST(gpio2_out, gpio2_out_pin, 1);
     GPIO_REQUEST(gpio3_in, gpio3_in_pin, 0);
     GPIO_REQUEST(gpio3_out, gpio3_out_pin, 1);
+    GPIO_REQUEST(my_wifi_led, wifi_led_pin, 1);
        return 0;
 }

        这样修改后就可以在/sys/class/jw_io_control目录下对文件节点my_wifi_led进行写操作,从而控制led灯的亮灭。

rockchip@ubuntu:/sys/class/jw_io_control$ ls
gpio0_in  gpio0_out  gpio1_in  gpio1_out  gpio2_in  gpio2_out  gpio3_in  gpio3_out  my_wifi_led  wifi_led  z32_reset  zigbee_led
rockchip@ubuntu:/sys/class/jw_io_control$ echo 1 > my_wifi_led 
rockchip@ubuntu:/sys/class/jw_io_control$ echo 0 > my_wifi_led 

3 驱动源码

3.1 驱动源码

        kernel/linux-5.10/drivers/jw/jw_gpio/jw_io_core.c驱动代码内容如下所示

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/* Debug */
#if 1
#define DBG(x...) printk(x)
#else
#define DBG(x...) do { } while (0)
#endif
 
#define GPIO_PIN_DEFINE(pin) unsigned int pin;

#define FUNC_GPIO_SHOW(name, p) \
static ssize_t name##_show(struct class *class,struct class_attribute *attr, char *buf) { \
    int value; \
    gpio_direction_input(p); \
    value = gpio_get_value(p); \
    return snprintf(buf, 3, "%d\n", value); \
}
   
#define FUNC_GPIO_STORE(name, p) \
static ssize_t name##_store(struct class *class,struct class_attribute *attr, const char *buf, size_t count) { \
    int enable; \
    sscanf(buf, "%d", &enable); \
    gpio_direction_output(p, enable ?  1 : 0); \
    return count; \
}

#define CLASS_GPIO_DEFINE(nm, pin) \
    GPIO_PIN_DEFINE(pin) \
    FUNC_GPIO_SHOW(nm, pin) \
    FUNC_GPIO_STORE(nm, pin) \
struct class_attribute class_attr_##nm =  { \
    .attr = { \
        .name = __stringify(nm), \
        .mode = 0666}, \
    .show = nm##_show, \
    .store = nm##_store,\
};

#define GPIO_REQUEST(name, pin, inout) { \
    ret = of_get_named_gpio_flags(node, #name, 0, &flags); \
        if (ret < 0) { \
                printk("%s() Can not read property" #name "\n", __FUNCTION__); \
        } else { \
                pin = ret; \
        printk(#name ": %d, state: %d\r\n", ret, !flags); \
                ret = devm_gpio_request(&pdev->dev, pin, #name); \
                if (ret < 0) { \
                        printk("%s() devm_gpio_request " #name " request ERROR\n", __FUNCTION__); \
                } else { \
            if (!inout) { \
                ret = gpio_direction_input(pin); \
                if (ret < 0) { \
                    printk("%s() gpio_direction_input " #name " set ERROR\n", __FUNCTION__); \
                } \
            } else {\
                ret = gpio_direction_output(pin, !flags); \
                if (ret < 0) { \
                    printk("%s() gpio_direction_output " #name " set ERROR\n", __FUNCTION__); \
                } \
            } \
        } \
        } \
    ret = class_create_file(g_jwio_class, &class_attr_##name); \
    if (ret) { \
        printk(KERN_ERR "%s:Fail to creat " #name " class file\n", __func__); \
        return ret; \
    } else { \
        printk("class_create jw_io_control " #name " OK !\n"); \
    }  \
}

static struct class *g_jwio_class; 

CLASS_GPIO_DEFINE(z32_reset, z32_reset_pin)
CLASS_GPIO_DEFINE(wifi_led, wifi_led_pin)
CLASS_GPIO_DEFINE(zigbee_led, zigbee_led_pin)

CLASS_GPIO_DEFINE(gpio0_in, gpio0_in_pin)
CLASS_GPIO_DEFINE(gpio0_out, gpio0_out_pin)
CLASS_GPIO_DEFINE(gpio1_in, gpio1_in_pin)
CLASS_GPIO_DEFINE(gpio1_out, gpio1_out_pin)
CLASS_GPIO_DEFINE(gpio2_in, gpio2_in_pin)
CLASS_GPIO_DEFINE(gpio2_out, gpio2_out_pin)
CLASS_GPIO_DEFINE(gpio3_in, gpio3_in_pin)
CLASS_GPIO_DEFINE(gpio3_out, gpio3_out_pin)
CLASS_GPIO_DEFINE(my_wifi_led, wifi_led_pin)

static int jw_io_control_probe(struct platform_device *pdev)
{
        struct device_node *node = pdev->dev.of_node;
        enum of_gpio_flags flags;
    static struct class *pclass; 
        int ret;

        printk(" #######jw_io_control_probe####### \n");

    pclass = class_create(THIS_MODULE, "jw_io_control");
    if (pclass == NULL) {
        printk(KERN_ERR "jw_io_control create failed!\r\n");
        return -1;
    }

    g_jwio_class = pclass;

    GPIO_REQUEST(z32_reset, z32_reset_pin, 1);
    GPIO_REQUEST(wifi_led, wifi_led_pin, 1);
    GPIO_REQUEST(zigbee_led, zigbee_led_pin, 1);

    GPIO_REQUEST(gpio0_in, gpio0_in_pin, 0);
    GPIO_REQUEST(gpio0_out, gpio0_out_pin, 1);
    GPIO_REQUEST(gpio1_in, gpio1_in_pin, 0);
    GPIO_REQUEST(gpio1_out, gpio1_out_pin, 1);
    GPIO_REQUEST(gpio2_in, gpio2_in_pin, 0);
    GPIO_REQUEST(gpio2_out, gpio2_out_pin, 1);
    GPIO_REQUEST(gpio3_in, gpio3_in_pin, 0);
    GPIO_REQUEST(gpio3_out, gpio3_out_pin, 1);
    GPIO_REQUEST(my_wifi_led, wifi_led_pin, 1);
        return 0;
}

static int jw_io_control_remove(struct platform_device *pdev)
{
        return 0;
}

static const struct of_device_id jw_io_control_of_match[] = {
        { .compatible = "jw_config", },
        {},
};
MODULE_DEVICE_TABLE(of, jw_io_control_of_match);

static struct platform_driver jw_io_control_driver = {
        .probe  = jw_io_control_probe,
        .remove = jw_io_control_remove,
        .driver = {
                .name   = "jw_config",
                .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(jw_io_control_of_match),
        },
};

static int __init jw_io_control_init(void)
{
        platform_driver_register(&jw_io_control_driver);
        return 0;
}

static void __exit jw_io_control_exit(void)
{
        platform_driver_unregister(&jw_io_control_driver);
}
late_initcall(jw_io_control_init);
MODULE_DESCRIPTION("jw config Driver");
MODULE_LICENSE("GPL");

3.2 设备树节点

219     jw_io_init {
220         compatible = "jw_config";
221         pinctrl-names = "default";
222         pinctrl-0 = <&jw_io_config >;
223         z32_reset = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>;
224         wifi_led = <&gpio4 RK_PC2 GPIO_ACTIVE_LOW>;
225         zigbee_led = <&gpio3 RK_PB1 GPIO_ACTIVE_LOW>;
226 
227         gpio0_in = <&gpio3 RK_PA4 GPIO_ACTIVE_LOW>;
228         gpio0_out = <&gpio3 RK_PA0 GPIO_ACTIVE_LOW>;
229         gpio1_in = <&gpio3 RK_PA5 GPIO_ACTIVE_LOW>;
230         gpio1_out = <&gpio3 RK_PA1 GPIO_ACTIVE_LOW>;
231         gpio2_in = <&gpio3 RK_PA6 GPIO_ACTIVE_LOW>;
232         gpio2_out = <&gpio3 RK_PA2 GPIO_ACTIVE_LOW>;
233         gpio3_in = <&gpio3 RK_PA7 GPIO_ACTIVE_LOW>;
234         gpio3_out = <&gpio3 RK_PA3 GPIO_ACTIVE_LOW>;
235 
236         my_wifi_led = <&gpio4 RK_PC2 GPIO_ACTIVE_LOW>;
237     };
435     jw_io_init {
436         jw_io_config: jw_io_config {
437             rockchip,pins = <1 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>,
438                             <4 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>,
439                             <3 RK_PB1 RK_FUNC_GPIO &pcfg_pull_up>,
440                             <3 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>,
441                             <3 RK_PA1 RK_FUNC_GPIO &pcfg_pull_up>,
442                             <3 RK_PA2 RK_FUNC_GPIO &pcfg_pull_up>,
443                             <3 RK_PA3 RK_FUNC_GPIO &pcfg_pull_up>,
444                             <3 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>,
445                             <3 RK_PA5 RK_FUNC_GPIO &pcfg_pull_up>,
446                             <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>,
447                             <3 RK_PA7 RK_FUNC_GPIO &pcfg_pull_up>;
448         };
449     };

3.3 驱动源码分析

3.3.1 ##解释

        ## 是一个特殊的预处理操作符,称为“标记粘贴”操作符。它的作用是在宏展开时,将两个宏参数(或宏参数与常量字符串)连接成一个单一的标识符。

3.3.2 class_create解释

        代码中的“pclass = class_create(THIS_MODULE, "jw_io_control");”会在 /sys/class/ 文件下创建一个jw_io_control 文件,class_create 函数分析如下所示。

函数原型

#define class_create(owner, name) \

({ \

static struct lock_class_key __key; \

__class_create(owner, name, &__key); \

})

struct class *__class_create(struct module *owner, const char *name, struct lock_class_key *key)

参数

struct module *owner

指向该设备类所属模块的指针。模块是内核中可加载和卸载的代码块,这个参数允许内核跟踪哪个模块创建了设备类。如果设备类是由内核核心代码(而非可加载模块)创建的,则此参数可能为 NULL

const char *name

设备类的名称

struct lock_class_key *key

指向锁类键的指针,用于锁调试。锁类键是内核中用于调试锁竞争和死锁问题的一种机制。如果不需要锁调试,此参数可以传递 NULL

返回值

struct class *

成功:class结构体指针 失败:NULL

功能

用于创建新的设备类。设备类的创建允许内核将具有相似功能的设备组织在一起,并提供了通过 /sys/class/ 目录与用户空间交互的接口

3.3.3 class_create_file解释

        以下代码中的“class_create_file(g_jwio_class, &class_attr_##name);”会在 /sys/class/jw_io_control/文件下创建一个 name 的文件。

pclass = class_create(THIS_MODULE, "jw_io_control")
g_jwio_class = pclass;
class_create_file(g_jwio_class, &class_attr_##name);

        class_create_file 函数分析如下所示。

函数原型

static inline int __must_check class_create_file(struct class *class, const struct class_attribute *attr)

参数

struct class *class

指向class结构体的指针,这个结构体代表了Linux中的一个设备类。在Linux中,设备被组织成类和设备,类是所有共享相同属性的设备的集合。例如,usb类包含了所有的USB设备

const struct class_attribute *attr

指向class_attribute结构体的指针,这个结构体定义了将要被创建的文件(属性)的属性,比如文件名、模式(只读、只写、可读可写)、显示和存储函数(用于从内核读取或写入内核的数据)等。

返回值

功能

1、在/sys/class//目录下为指定的类创建一个新的属性文件。这个文件允许用户空间程序读取或修改内核中与该类相关的某些信息或设置。

2、创建的文件名由class_attribute结构体中的attr.name字段指定。

3、文件的操作(如读取或写入)通过class_attribute结构体中定义的show和store回调函数实现。

3.3.4 of_get_named_gpio_flags解释

        of_get_named_gpio_flags 函数的分析如下所示。

函数原型

int of_get_named_gpio_flags(struct device_node *np, const char *list_name, int index, enum of_gpio_flags *flags)

参数

struct device_node *np

设备树节点指针

const char *list_name

GPIO属性名称

int index

属性值的索引

enum of_gpio_flags *flags

用于保存GPIO属性值的标志

返回值

int

成功:返回GPIO号 失败:负数

功能

从设备树中获取指定名称的GPIO列表中第index个GPIO的编号以及可能的标志

3.3.5 devm_gpio_request解释

        devm_gpio_request 函数的分析如下所示,该函数通过结合 devres 资源管理机制来简化 GPIO 的分配和释放过程,确保在设备驱动程序卸载时,GPIO 能够自动释放,减少了手动释放的负担。

函数原型

int devm_gpio_request(struct device *dev, unsigned gpio, const char *label)

参数

struct device *dev

设备结构体指针

unsigned gpio

GPIO号

const char *label

用于表示请求的GPIO引脚

返回值

int

成功:0 失败:负数

功能

请求一个GPIO引脚设备使用

        devm_gpio_request 函数在内核源码中的原型如下所示。

364 /**                                  
365  *      devm_gpio_request - request a GPIO for a managed device
366  *      @dev: device to request the GPIO for
367  *      @gpio: GPIO to allocate
368  *      @label: the name of the requested GPIO
369  *
370  *      Except for the extra @dev argument, this function takes the
371  *      same arguments and performs the same function as
372  *      gpio_request().  GPIOs requested with this function will be
373  *      automatically freed on driver detach.
374  *
375  *      If an GPIO allocated with this function needs to be freed
376  *      separately, devm_gpio_free() must be used.
377  */
378 
379 int devm_gpio_request(struct device *dev, unsigned gpio, const char *label)
380 {
381         unsigned *dr;
382         int rc;
383 
384         dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL);
385         if (!dr)
386                 return -ENOMEM;
387 
388         rc = gpio_request(gpio, label);
389         if (rc) {
390                 devres_free(dr);
391                 return rc;       
392         }                        
393 
394         *dr = gpio;
395         devres_add(dev, dr);
396 
397         return 0;         
398 }

3.3.6 platform_driver_register解释

        platform_driver_register 函数的分析如下所示。

函数原型

#define platform_driver_register(drv) __platform_driver_register(drv, THIS_MODULE)

参数

struct platform_driver *drv

指向 platform_driver 结构的指针。platform_driver 结构定义了平台驱动的相关信息和操作

返回值

功能

用于注册平台设备驱动

        __platform_driver_register 函数在内核源码中的原型如下所示。

648 int __platform_driver_register(struct platform_driver *drv,
 649                                 struct module *owner)
 650 {
 651         drv->driver.owner = owner;     
 652         drv->driver.bus = &platform_bus_type;
 653         drv->driver.probe = platform_drv_probe;
 654         drv->driver.remove = platform_drv_remove;
 655         drv->driver.shutdown = platform_drv_shutdown;
 656 
 657         return driver_register(&drv->driver);
 658 }

3.3.7 platform_driver_unregister解释

        platform_driver_unregister 函数的分析如下所示。

函数原型

void platform_driver_unregister(struct platform_driver *drv)

参数

struct platform_driver *drv

指向 platform_driver 结构的指针。platform_driver 结构定义了平台驱动的相关信息和操作

返回值

功能

用于注销平台设备驱动

        platform_driver_unregister 函数在内核源码中的原型如下所示。

 665 void platform_driver_unregister(struct platform_driver *drv)
 666 {
 667         driver_unregister(&drv->driver);
 668 }
190 void driver_unregister(struct device_driver *drv)
191 {                                
192         if (!drv || !drv->p) {
193                 WARN(1, "Unexpected driver unregister!\n");
194                 return;
195         }
196         driver_remove_groups(drv, drv->groups);
197         bus_remove_driver(drv); 
198 }

你可能感兴趣的:(linux内核的系统实战,linux驱动,linux驱动,led灯驱动)