设备树下Led驱动实验-设备树下Led驱动代码完善

发布时间:2024年01月18日

一. 简介

前面几篇文章学习了向设备树文件中添加 Led设备节点信息。Led驱动代码框架搭建。文章地址如下:

设备树下Led驱动实验-向设备树文件添加Led设备节点-CSDN博客

设备树下Led驱动实验-Led驱动代码框架搭建-CSDN博客

本文继续进行 Led驱动实现。主要实现读取设备树文件中的寄存器地址,进行初始化,最后,实现Led灯的打开与关闭。

实现效果:通过应用程序调用 Led驱动,实现 Led灯的开与关。

二.? 设备树下Led驱动代码完善

这里在上一篇文章所实现的字符设备驱动框架代码的基础上,添加读取设备树中 Led设备节点信息,同时,进行Led的 IO的初始化工作,与开关灯的功能。

dtsled.c文件中的代码实现如下:

#include <linux/module.h>
#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>

#define DEV_NAME   "alpha_led" 
#define DEV_CNT    1 
#define ON_LED     1
#define OFF_LED    0

//地址映射后寄存器的虚拟地址指针
static void __iomem *  IMX6ULL_CCM_CCGR1;
static void __iomem *  IMX6ULL_SW_MUX_GPIO1_IO03;
static void __iomem *  IMX6ULL_SW_PAD_GPIO01_IO03;
static void __iomem *  IMX6ULL_GDIR;
static void __iomem *  IMX6ULL_DR;

//Led设备结构体
struct dtsled_dev {
    dev_t devid; //设备号(主+次设备号)
    int major;   //主设备号
    int minor;   //次设备号
    struct cdev cdev;
    struct class* class;
    struct device * dev;
     struct device_node * dev_node;
};

struct dtsled_dev dtsled;

void contrl_led(unsigned int status)
{
    unsigned int reg_value = 0;
    if(OFF_LED == status) //关闭Led灯
    {
        reg_value = readl(IMX6ULL_DR);
        reg_value |= (1 << 3); //写入1,关闭Led
        writel(reg_value, IMX6ULL_DR);
    }
    else if(ON_LED == status) //打开Led灯
    {
        reg_value = readl(IMX6ULL_DR);
        reg_value &= ~(1 << 3); //写入0,打开Led
        writel(reg_value, IMX6ULL_DR);
    }
}

/*打开设备函数 */
static int dtsled_open(struct inode * node, struct file * file)
{
//    file->private_data = &dtsled;
    return 0;
}

/*写数据函数*/
static ssize_t dtsled_write(struct file * file, const char __user * buf, size_t count, loff_t * opt)
{
    int ret = 0;
    int rev_buf[2] = {0};
//    struct dtsled_dev * led = (struct dtsled_dev*)(file->private_data);
    ret = copy_from_user(rev_buf, buf, count);
    if(ret < 0)
    {
        printk("led_write failed!\r\n");
        return -EFAULT;
    }
    contrl_led(rev_buf[0]);
    return 0;
}

/*关闭设备函数 */
static int dtsled_release(struct inode * node, struct file * file)
{
//    struct dtsled_dev* led = (struct dtsled_dev*)(file->private_data);
    return 0;
}

//字符设备的函数操作集
const struct file_operations dtsled_fops = {
    .owner = THIS_MODULE,   
    .open = dtsled_open,
    .write = dtsled_write,
    .release = dtsled_release,
};

/*驱动入口函数 */
static int __init dtsled_init(void)
{
    int ret = 0;
    unsigned int buf[20] = {0};
    int i = 0;
    u32 reg_value = 0;

    /*1. 读取设备树文件中寄存器地址 */
    dtsled.dev_node = of_find_node_by_path("/alpha_led");
    if(NULL == dtsled.dev_node)
    {
        printk("find dev-node failed!\n");
        ret = -EINVAL;
        goto apply_devid_failed;
    }
    ret = of_property_read_u32_array(dtsled.dev_node,  "reg", buf, 10);
    if(ret < 0)
    {
        printk("read dev_node'data failed!\n");
        goto apply_devid_failed;
    }
	for(i=0; i<10; i++)
    {
        printk("0X%X ", buf[i]);
    }	
    printk("\n");			   
					    
    /*2. 物理地址转换为虚拟地址*/
    IMX6ULL_CCM_CCGR1 =  ioremap(buf[0], buf[1]);
    IMX6ULL_SW_MUX_GPIO1_IO03 =  ioremap(buf[2], buf[3]);
    IMX6ULL_SW_PAD_GPIO01_IO03 =  ioremap(buf[4], buf[5]);
    IMX6ULL_GDIR =  ioremap(buf[6], buf[7]);
    IMX6ULL_DR =  ioremap(buf[8], buf[9]);

    /*3. Led设备IO初始化 */
    //使能时钟
    reg_value = readl(IMX6ULL_CCM_CCGR1);
    reg_value &= ~(3 << 26);
    reg_value |= (3 << 26);
    writel(reg_value, IMX6ULL_CCM_CCGR1);
    //复用为GPIO功能
    writel(0X05, IMX6ULL_SW_MUX_GPIO1_IO03);
    //配置电气特性
    writel(0X10B0, IMX6ULL_SW_PAD_GPIO01_IO03);
    //设置为输出
    reg_value = readl(IMX6ULL_GDIR);
    reg_value &= ~(1 << 3);
    reg_value |= (1 << 3);
    writel(reg_value, IMX6ULL_GDIR);
    
    //关闭Led灯
    reg_value = readl(IMX6ULL_DR);
    reg_value |= (1 << 3); //置1,关闭Led
    writel(reg_value, IMX6ULL_DR);

    dtsled.major = 0;
    /*1. 申请设备号 */
    if(dtsled.major) //给定设备号
    {
        dtsled.devid = MKDEV(dtsled.major, 0);
        ret = register_chrdev_region(dtsled.devid, DEV_CNT, DEV_NAME);
    }
    else //向内核申请设备号
    {
        ret = alloc_chrdev_region(&dtsled.devid, 0, DEV_CNT, DEV_NAME);
        dtsled.major = MAJOR(dtsled.devid);
        dtsled.minor = MINOR(dtsled.devid);
        printk("dtsled.major: %d\n", dtsled.major);
        printk("dtsled.minor: %d\n", dtsled.minor);
    }
    if(ret < 0)
    {
        printk("apply_dev_numbers failed!\n");
        goto apply_devid_failed;        
    }

    /*2. 添加字符设备 */
    dtsled.cdev.owner = THIS_MODULE;
    cdev_init(&dtsled.cdev, &dtsled_fops);
    ret = cdev_add(&dtsled.cdev, dtsled.devid, DEV_CNT);
    if(ret < 0)
    {
        printk("cdev-add failed!\n");
        goto cdev_add_failed;
    }

    /*3. 自动创建设备节点 */
    //创建类
    dtsled.class = class_create(THIS_MODULE, DEV_NAME);
    if (IS_ERR(dtsled.class)) {
        printk("class_create failed!\n");
		ret = PTR_ERR(dtsled.class);
		goto auto_class_create_failed;
	}
    //创建设备 
    dtsled.dev = device_create(dtsled.class, NULL, dtsled.devid, NULL, DEV_NAME);
    if (IS_ERR(dtsled.dev)) {
        printk("device_create failed!\n");
		ret = PTR_ERR(dtsled.dev);
		goto auto_create_dev_failed;
	}

    return 0;

auto_create_dev_failed:
    class_destroy(dtsled.class);
auto_class_create_failed:
    cdev_del(&dtsled.cdev);
cdev_add_failed:
    unregister_chrdev_region(dtsled.devid, DEV_CNT);
apply_devid_failed:
    return ret;
}

/*驱动出口函数 */
static void __exit dtsled_exit(void)
{
    /*取消地址映射*/
    iounmap(IMX6ULL_CCM_CCGR1);
    iounmap(IMX6ULL_SW_MUX_GPIO1_IO03);
    iounmap(IMX6ULL_SW_PAD_GPIO01_IO03);
    iounmap(IMX6ULL_GDIR);
    iounmap(IMX6ULL_DR);

    /*1.删除字符设备 */
    cdev_del(&dtsled.cdev);
    /*2.注销设备号 */
    unregister_chrdev_region(dtsled.devid, DEV_CNT);
    /*3. 摧毁设备 */
    device_destroy(dtsled.class, dtsled.devid);
    /*4. 摧毁类*/
    class_destroy(dtsled.class);
}

/*模块入口与出口*/
module_init(dtsled_init);
module_exit(dtsled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LingXiaoZhan");

三.? 编译驱动程序

通过 ubuntu终端进入 5_dtsled工程目录下,编译驱动程序:

wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/5_dtsled.c$ 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/5_dtsled.c 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/5_dtsled.c/dtsled.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/wangtian/zhengdian_Linux/Linux_Drivers/5_dtsled.c/dtsled.mod.o
  LD [M]  /home/wangtian/zhengdian_Linux/Linux_Drivers/5_dtsled.c/dtsled.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/5_dtsled.c$

可以看出,驱动程序已经编译成功。下面就是进行 驱动程序的测试。

文章来源:https://blog.csdn.net/wojiaxiaohuang2014/article/details/135624532
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。