有设备树的plaform驱动实验:驱动注册框架添加Led字符驱动代码

一.  简介

前面文章实现了 有设备树的plaform驱动实验中,platform_driver驱动注册框架。文章地址如下:

有设备树的plaform驱动实验:platform_driver驱动注册框架-CSDN博客

本文继续进行有设备树的plaform驱动实验,具体向 platform_driver驱动注册框架添加 Led灯的驱动代码。

Led驱动代码包括:Led字符设备驱动代码及 Led初始化工作代码(有设备树情况下的Led的IO初始化工作),Led灯 file_operation函数集。

二.  有设备树的plaform驱动实验:驱动注册框架添加Led字符驱动代码

1.  思路

前面一篇文章实现了 platform_driver驱动注册框架,设备与驱动匹配工作。这里开始向 框架中添加 Led驱动代码。

因为是有设备树支持的,也就是设备树描述了设备节点信息。Led驱动代码部分也就可以直接使用 6_gpioled(通过访问设备节点的 of函数与 gpio子系统提供的 API函数)实验中代码实现。

2.  驱动注册框架添加Led字符驱动代码

(1)  修改项

将 6_gpioled 实验中的 Led驱动代码添加到这里 16_dts_platform工程中。具体添加到 platform_driver 驱动注册框架中。

驱动和设备匹配成功以后,设备节点信息就会从设备树节点转为 platform_device结构体。

platform_device结构体中 成员 dev,包括一个 device_node结构体成员。

基于以上原因,可以优化的或更改的Led灯初始化代码如下:

    /*读取设备节点*/
    gpioled.dev_node = of_find_node_by_path("/gpioled");
    if(NULL == gpioled.dev_node)
    {
        printk("find dev_node failed!\n");
        goto read_devnode_failed;
    }

可以直接通过 platform_device结构体获取到 Led设备节点,代码如下:

    /*读取设备节点*/
    gpioled.dev_node = platform_dev->dev.of_node; 

(2)  驱动注册框架添加Led字符驱动代码

16_dts_platform工程中 platform_leddriver.c 代码如下:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/types.h>
#include <linux/platform_device.h>

#define  LED_OFF     0
#define  LED_ON      1
#define  GPIOLED_NAME  "gpioled"
#define  GPIOLED_CNT   1


//Led设备结构体
struct gpio_led {
    dev_t devid;
    int major;
    int minor;
    struct cdev cdev;
    struct class* class;
    struct device* dev;
    struct device_node * dev_node;
    int gpio_number;
};

struct gpio_led gpioled;

static int gpioled_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &gpioled;
    return 0;
}

static ssize_t gpioled_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
    int ret = 0;
    char rx_buf[2] = {0};

    ret = copy_from_user(rx_buf, buf, count);
    if(ret != 0)
    {
        printk("copy_from_user failed!\n");
    }
    if(rx_buf[0] == LED_ON)
    {    
        //设置低电平,打开Led
        gpio_set_value(gpioled.gpio_number, 0);  
    }
    else if(rx_buf[0] == LED_OFF)
    {   
        //设置高电平,关闭Led
        gpio_set_value(gpioled.gpio_number, 1);
    }
    return 0;
}

static int gpioled_close(struct inode *inode, struct file *filp)
{
    return 0;
}

const struct file_operations gpioled_fops = {
    .owner = THIS_MODULE,
    .open = gpioled_open,
	.release = gpioled_close,
    .write = gpioled_write,
};

int platform_probe(struct platform_device * platform_dev)
{
    int ret = 0;
    printk("platform_probe!\n");
    /*1.注册设备号*/
    gpioled.major = 0;
    if(gpioled.major) //给出主设备号
    {
        gpioled.devid = MKDEV(gpioled.major, 0);
        ret = register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);  
    }
    else //未给设备号时,向Linux申请设备号
    {
        ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);
        gpioled.major = MAJOR(gpioled.devid);
        gpioled.minor = MINOR(gpioled.devid);
    }
    printk("gpioled.major: %d\n", gpioled.major);
    printk("gpioled.minor: %d\n", gpioled.minor);
    if(ret < 0)
    {
        printk("apply dev_number failed!\n");
        goto apply_devid_failed;
    }
    
    /*2.设备初始化 */
    gpioled.cdev.owner = THIS_MODULE;
    cdev_init(&gpioled.cdev, &gpioled_fops);
    ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
    if(ret < 0)
    {
        printk("cdev_add failed\n");
        goto cdev_add_failed;
    }

    /*3.自动创建设备节点*/
    gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
    if (IS_ERR(gpioled.class)) {
		ret = PTR_ERR(gpioled.class);
		goto class_create_failed;
	}
    gpioled.dev = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
    if (IS_ERR(gpioled.dev)) {
		ret = PTR_ERR(gpioled.dev);
		goto dev_create_failed;
	}	

    /*读取设备节点*/
    gpioled.dev_node = platform_dev->dev.of_node; 
    /*获取Led所对应的GPIO的编号*/
    gpioled.gpio_number = of_get_named_gpio(gpioled.dev_node, "led-gpio", 0);
    if(gpioled.gpio_number < 0)
    {
        printk("get_named_gpio failed!\n");
        goto read_devnode_failed;
    }
    /*申请GPIO管脚 */
    ret = gpio_request(gpioled.gpio_number, GPIOLED_NAME);
    if(ret != 0)
    {
        printk("gpio_request failed!\n");
        goto read_devnode_failed;
    }
    /*设置GPIO为输出,设置为高电平,关闭Led */
    ret = gpio_direction_output(gpioled.gpio_number, 1);
    if(ret != 0)
    {
        printk("gpio_direction_input failed!\n");
        goto set_gpio_input_failed;
    }
    return 0;

set_gpio_input_failed:
    gpio_free(gpioled.gpio_number);
read_devnode_failed:
    device_destroy(gpioled.class, gpioled.devid);
dev_create_failed:
    class_destroy(gpioled.class);
class_create_failed:
    cdev_del(&gpioled.cdev);
cdev_add_failed:
    unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
apply_devid_failed:
    return ret;
}

int platform_remove(struct platform_device * platform_dev)
{
    printk("platform_remove!\n");
    //关闭Led灯
    gpio_set_value(gpioled.gpio_number, 1);
    /*释放IO编号 */
    gpio_free(gpioled.gpio_number);

    /*1. 删除设备*/
    cdev_del(&gpioled.cdev);
    /*2. 注销设备号*/
    unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
    //3. 销毁设备
    device_destroy(gpioled.class, gpioled.devid);
    /*4. 销毁类*/
    class_destroy(gpioled.class);
    return 0;
}

//of_device_id结构体,很重要!
//注意 of_device_id结构体的成员最后一个写空
struct of_device_id of_dev_id[] = {
    //与设备树中Led节点的compatible一致,有设备树时驱动与设备匹配条件
    { .compatible = "alientek, gpioled", },
    {/*Sentinel */}, 
};

//Platform_driver结构体
struct platform_driver plat_leddriver = {
    .driver = {
        .name = "imx6ull_led", //驱动名,在无设备树时驱动与设备匹配的条件
        .of_match_table = of_dev_id,
    },

    .probe = platform_probe,
    .remove = platform_remove,
};


/*模块加载 */
static int __init platform_driver_init(void)
{
    //注册驱动
    return platform_driver_register(&plat_leddriver);
}

/*模块卸载 */
static void __exit platform_driver_exit(void)
{
    //卸载驱动
    platform_driver_unregister(&plat_leddriver);
}

/*驱动加载与卸载 */
module_init(platform_driver_init);
module_exit(platform_driver_exit);

MODULE_LICENSE("GPL"); //模块 Licence
MODULE_AUTHOR("WeiWuXian"); //作者

三.  编译驱动代码

对驱动代码进行模块化编译:

wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/16_dts_platform$ make
make -C /home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/wangtian/zhengdian_Linux/Linux_Drivers/16_dts_platform modules
make[1]: 进入目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”
  CC [M]  /home/wangtian/zhengdian_Linux/Linux_Drivers/16_dts_platform/platform_leddriver.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/wangtian/zhengdian_Linux/Linux_Drivers/16_dts_platform/platform_leddriver.mod.o
  LD [M]  /home/wangtian/zhengdian_Linux/Linux_Drivers/16_dts_platform/platform_leddriver.ko
make[1]: 离开目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/16_dts_platform$ 

可以看出,驱动模块可以编译通过。

接下来对驱动功能进行测试。led是否可以实现亮灭。

  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值