• 荔枝派zero驱动开发06:GPIO操作(platform框架)


    参考:
    正点原子Linux第五十四章 platform设备驱动实验
    一张图掌握 Linux platform 平台设备驱动框架

    上一篇:荔枝派zero驱动开发05:GPIO操作(使用GPIO子系统)
    下一篇:更新中…

    概述

    platform是一种分层思想,所谓的 platform 驱动并不是独立于字符设备驱动、块设备驱动和网络设备驱动之外的其他种类的驱动;platform 只是为了驱动的分离与分层而提出来的一种框架,其驱动的具体实现还是需要字符设备驱动、块设备驱动或网络设备驱动。

    • 初学对具体设计的意图无需完全掌握,大致掌握运行流,在内核驱动中可以找到和读懂相关代码即可,用户编写驱动可以参考模板

    • 参考下图,一图流,完全弄懂这张图就能完全掌握platform框架的思想 😃

    在这里插入图片描述
    图源:一张图掌握 Linux platform 平台设备驱动框架!

    设备树修改

    设备树直接使用上一章即可,无需修改

    简要分析

    在上一章源码基础上修改,添加platform相关的数据结构和匹配表,实现led_probe(即原驱动初始化函数),实现led_remove(原退出函数),并将驱动初始化改为platform_driver_register和platform_driver_unregister

    //platform相关
    // 匹配列表
    static const struct of_device_id led_of_match[] = {
    	{ .compatible = "user,led" },
    	{ /* Sentinel */ }
    };
    MODULE_DEVICE_TABLE(of, led_of_match);
    
    /* platform驱动结构体 */
    static struct platform_driver led_driver = {
    	.driver		= {
    		.name	= "platform-led",			// 驱动名字,将在/sys/bus/platform/drivers/下生成 
    		.of_match_table	= led_of_match,     // 设备树匹配表
    	},
    	.probe		= led_probe,
    	.remove		= led_remove,
    };
    
    static int __init leddriver_init(void)
    {
    	return platform_driver_register(&led_driver);
    }
    
    static void __exit leddriver_exit(void)
    {
    	platform_driver_unregister(&led_driver);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    如上,设备树与led_of_match的属性值匹配后,即执行led_probe函数,同理:卸载驱动时会执行led_remove函数,卸载操作无需改动

    函数原型:int (*probe)(struct platform_device *);

    led_probe函数参考实现:

    static int led_probe(struct platform_device *pdev)
    {
        int ret;
        const char *str;
    
        printk("led driver and device was matched!\r\n");
    
        // 获取 LED 灯的 GPIO 号
        // 进probe说明设备树已匹配,直接使用设备树节点即可
        platform_led.led_gpio = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);
        if (platform_led.led_gpio < 0)
        {
            printk("can't get gpios");
            return -EINVAL;
        }
        printk("gpio num = %d\r\n", platform_led.led_gpio);
    
        // 向 gpio 子系统申请使用 GPIO
        ret = gpio_request(platform_led.led_gpio, "green");	
        // 设置 PI0 为输出,并且输出低电平,默认打开 LED 灯
        ret = gpio_direction_output(platform_led.led_gpio, 0);
       	...
       	
        // 注册字符设备
    	...
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    这里直接使用pdev->dev.of_node引用设备节点即可

    后续注册字符设备、操作函数ops等无改动,不再赘述

    测试

    在这里插入图片描述

    chardevApp同样使用上一章的测试APP,功能正常实现

    源码

    platform_led.c

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    struct platform_led_dev
    {
        dev_t devid;
        struct cdev cdev;
        struct class *class;
        struct device *device;
        int major;
        int minor;
        struct device_node *nd;
        int led_gpio;
    };
    struct platform_led_dev platform_led = {
        .major = 0,
    };
    
    #define PIN_N 0 // 第0个引脚,PG0,绿色
    #define DEV_NAME "platform_led"
    #define LED_ON 0 // 上拉,低电平亮
    #define LED_OFF 1
    
    static int led_gpio_open(struct inode *inode, struct file *file)
    {
        file->private_data = &platform_led;
        return 0;
    }
    
    static int led_gpio_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
    {
        return 0;
    }
    
    static int led_gpio_release(struct inode *inode, struct file *file)
    {
        return 0;
    }
    
    static ssize_t led_gpio_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
    {
        int ret = 0;
        unsigned char databuf;
        struct platform_led_dev *dev = file->private_data;
    
        ret = copy_from_user(&databuf, user_buf, sizeof(databuf));
        if (ret < 0)
        {
            pr_err("copy_from_user failed\r\n");
            return -EFAULT;
        }
        if (databuf == 0 || databuf == '0') // LED_OFF
            gpio_set_value(dev->led_gpio, 1);
        if (databuf == 1 || databuf == '1') // LED_ON
            gpio_set_value(dev->led_gpio, 0);
    
        return 1;
    }
    
    static const struct file_operations platform_led_fops = {
        .open = led_gpio_open,
        .read = led_gpio_read,
        .release = led_gpio_release,
        .write = led_gpio_write,
    };
    
    static int led_probe(struct platform_device *pdev)
    {
        int ret;
        const char *str;
    
        printk("led driver and device was matched!\r\n");
    
        // 获取 LED 灯的 GPIO 号
        // 进probe说明设备树已匹配,直接使用设备树节点即可
        platform_led.led_gpio = of_get_named_gpio(pdev->dev.of_node, "gpios", 0);
        if (platform_led.led_gpio < 0)
        {
            printk("can't get gpios");
            return -EINVAL;
        }
        printk("gpio num = %d\r\n", platform_led.led_gpio);
    
        // 向 gpio 子系统申请使用 GPIO
        ret = gpio_request(platform_led.led_gpio, "green");
        if (ret)
        {
            printk(KERN_ERR "Failed to request gpio\n");
            return ret;
        }
    
        // 设置 PI0 为输出,并且输出低电平,默认打开 LED 灯
        ret = gpio_direction_output(platform_led.led_gpio, 0);
        if (ret < 0)
        {
            printk("can't set gpio!\r\n");
        }
    
        // 注册字符设备
        if (platform_led.major) // 定义了设备号,静态设备号
        {
            platform_led.devid = MKDEV(platform_led.major, 0);
            ret = register_chrdev_region(platform_led.major, 1, DEV_NAME);
            if (ret < 0)
            {
                pr_err("cannot register %s char driver.ret:%d\r\n", DEV_NAME, ret);
                goto exit;
            }
        }
        else // 没有定义设备号,动态申请设备号
        {
            ret = alloc_chrdev_region(&platform_led.devid, 0, 1, DEV_NAME);
            if (ret < 0)
            {
                pr_err("cannot alloc_chrdev_region,ret:%d\r\n", ret);
                goto exit;
            }
            platform_led.major = MAJOR(platform_led.devid);
            platform_led.minor = MINOR(platform_led.devid);
        }
        printk("led major=%d,minor=%d\r\n", platform_led.major, platform_led.minor);
    
        platform_led.cdev.owner = THIS_MODULE;
        cdev_init(&platform_led.cdev, &platform_led_fops);
    
        ret = cdev_add(&platform_led.cdev, platform_led.devid, 1);
        if (ret < 0)
            goto del_unregister;
    
        platform_led.class = class_create(THIS_MODULE, DEV_NAME);
        if (IS_ERR(platform_led.class))
            goto del_cdev;
    
        platform_led.device = device_create(platform_led.class, NULL, platform_led.devid, NULL, DEV_NAME);
        if (IS_ERR(platform_led.device))
            goto destroy_class;
    
        return 0;
    
        // 注意  goto后的标签没有return操作,将顺序执行多个label直至return,这里反向写
    destroy_class:
        class_destroy(platform_led.class);
    del_cdev:
        cdev_del(&platform_led.cdev);
    del_unregister:
        unregister_chrdev_region(platform_led.devid, 1);
    exit:
        printk("init failed\r\n");
        return -EIO;
    }
    
    static int led_remove(struct platform_device *pdev)
    {
        if (platform_led.led_gpio)
        {
            gpio_set_value(platform_led.led_gpio, 1);
            gpio_free(platform_led.led_gpio);
        }
        cdev_del(&platform_led.cdev);
        unregister_chrdev_region(platform_led.devid, 1);
        device_destroy(platform_led.class, platform_led.devid);
        class_destroy(platform_led.class);
        return 0;
    }
    
    
    
    //platform相关
    // 匹配列表
    static const struct of_device_id led_of_match[] = {
    	{ .compatible = "user,led" },
    	{ /* Sentinel */ }
    };
    
    MODULE_DEVICE_TABLE(of, led_of_match);
    
    /* platform驱动结构体 */
    static struct platform_driver led_driver = {
    	.driver		= {
    		.name	= "platform-led",			// 驱动名字,将在/sys/bus/platform/drivers/下生成 
    		.of_match_table	= led_of_match,     // 设备树匹配表
    	},
    	.probe		= led_probe,
    	.remove		= led_remove,
    };
    		
    static int __init leddriver_init(void)
    {
    	return platform_driver_register(&led_driver);
    }
    
    static void __exit leddriver_exit(void)
    {
    	platform_driver_unregister(&led_driver);
    }
    
    module_init(leddriver_init);
    module_exit(leddriver_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("USER");
    MODULE_INFO(intree, "Y");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
  • 相关阅读:
    OS模块中获取当前文件的绝对路径的相关方法
    Windows内核函数 - ANSI_STRING字符串与UNICODE_STRING字符串
    java spring cloud 企业工程管理系统源码+二次开发+定制化服务
    StringBuffer与StringBuilder[37]
    使用pytorch实现一个线性回归训练函数
    Flink Operator 使用指南 之 全局配置
    Apache Spark 的基本概念和在大数据分析中的应用
    BUUCTF crypto做题记录(8)新手向
    leetcode刷题笔记——使用双指针处理链表问题
    使用SPARK进行特征工程
  • 原文地址:https://blog.csdn.net/my_fly_dream/article/details/136572955