设备树下的 platform 驱动编写

该程序展示了如何在Linux内核中用设备树来替代platform设备驱动中的leddevice.c部分,通过设备树匹配方法更新platform_driver结构体。主要操作包括:打开、写入和关闭LED设备,以及通过of_get_named_gpio获取GPIO引脚并进行配置。当设备被探测到时,会创建设备节点,初始化cdev,并在设备移除时清理资源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        本实验是在platform设备驱动试验的基础上进行改变的,将platform设备驱动试验中的设备程序leddevice.c部分使用设备树代替。程序中的platform_driver结构体内容增加设备树匹配方式。

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/mach/map.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/timer.h>
#include <linux/platform_device.h>

#define GPIOLED_CNT 1
#define GPIOLED_NAME "dtsplatformled"

struct gpioled_dev {
    int major;
    int minor;
    dev_t devide;//设备号
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node * bl_nd1;/*设备节点*/
    int led_gpio;/*属性编号*/
};
struct gpioled_dev gpioled;

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
 static int gpioled_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &gpioled; /* 设置私有数据 */
	printk("chrdevbase open!\r\n");
	return 0;
}

/*
 * @description		: 向设备写数据 
 * @param - filp 	: 设备文件,表示打开的文件描述符
 * @param - buf 	: 要写给设备写入的数据
 * @param - cnt 	: 要写入的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t gpioled_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    struct gpioled_dev *dev=(struct gpioled_dev * )filp->private_data;
    int retvalue;
    unsigned char databuf[1];
    unsigned char ledstat;
    retvalue = copy_from_user(databuf, buf, cnt);
    if(retvalue < 0) {
        printk("kernel write failed!\r\n");
        return -EFAULT;
    }

    ledstat = databuf[0]; /* 获取状态值 */
    if(ledstat == 0) { 
       gpio_set_value(dev->led_gpio,0); /* 打开 LED 灯 */
    } else if(ledstat == 1) {
       gpio_set_value(dev->led_gpio,1); /* 关闭 LED 灯 */
    }
 
	return 0;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int gpioled_release(struct inode *inode, struct file *filp)
{
	//printk("chrdevbase release!\r\n");
	return 0;
}
static struct file_operations gpioled_ops={
    .owner=THIS_MODULE,
    .open=gpioled_open,
    .release=gpioled_release,
    .write=gpioled_write,
};

static int led_probe(struct platform_device *dev){
    printk("led_probe\r\n");
     int ret=0;
    #if 0
    /*获取设备节点*/
    gpioled.bl_nd1=of_find_node_by_path("/gpioled");
    if(gpioled.bl_nd1==NULL)
    {
        printk("of_find_node_by_path failed\r\n");
        return -EINVAL;
    }
    #endif
    gpioled.bl_nd1=dev->dev.of_node;

    /*获取设备cd-gpio属性的第一个GPIO编号*/
    gpioled.led_gpio=of_get_named_gpio(gpioled.bl_nd1,"cd-gpio",0);
    if(gpioled.led_gpio<0)
    {
        printk("of_get_named_gpio failed\r\n");
        return -EINVAL;
    }

    /*申请一个GPIO管脚*/
    ret=gpio_request(gpioled.led_gpio,"cd-gpio");
    if(ret==0){
        printk("gpio_requests success\n");
    }
    /*将GPIO设置为输出,默认输出高电平*/
    ret=gpio_direction_output(gpioled.led_gpio,1);
    if(ret<0)
    {
        printk("gpio_direction_output failed\n");
    }

    /*注册设备号*/
    gpioled.major=0;
    if(gpioled.major){
        gpioled.devide=MKDEV(gpioled.major,0);
        register_chrdev_region(gpioled.devide,GPIOLED_CNT,GPIOLED_NAME);
    }else{
        ret=alloc_chrdev_region(&gpioled.devide, 0, GPIOLED_CNT, GPIOLED_NAME);	/* 申请设备号 */
		if(ret<0)
            {
                goto fail_devide;
            }
        gpioled.major = MAJOR(gpioled.devide);	/* 获取分配号的主设备号 */
		gpioled.minor = MINOR(gpioled.devide);	/* 获取分配号的次设备号 */
        printk("major=%d\n",gpioled.major);
        printk("minor=%d\n",gpioled.minor);
    }

     /*初始化cdev*/
    gpioled.cdev.owner=THIS_MODULE;
    cdev_init(&gpioled.cdev, &gpioled_ops);
    	
	/* 3、添加一个cdev */
	ret= cdev_add(&gpioled.cdev, gpioled.devide, GPIOLED_CNT);
    if(ret<0)
        {
            goto fail_cdev;
        }

    //创建设备节点
	/* 4、创建类 */
	gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
	if (IS_ERR(gpioled.class)) {
		ret= PTR_ERR(gpioled.class);
        goto fail_class;
	}

	/* 5、创建设备 */
	gpioled.device = device_create(gpioled.class, NULL, gpioled.devide, NULL, GPIOLED_NAME);
	if (IS_ERR(gpioled.device)) {
		ret=PTR_ERR(gpioled.device);
        goto fail_device;
	}    
    return 0;
    fail_device:
        class_destroy(gpioled.class);
    fail_class:
        cdev_del(&gpioled.cdev);
    fail_cdev:
        unregister_chrdev_region(gpioled.devide, GPIOLED_CNT);
    fail_devide:
        return ret;
}

static int led_remove(struct platform_device *dev){
    printk("led_remove\r\n");
    gpio_set_value(gpioled.led_gpio,1);
    device_destroy(gpioled.class,gpioled.devide);
    
    class_destroy(gpioled.class);
    cdev_del(&gpioled.cdev);

    gpio_free(gpioled.led_gpio);
    unregister_chrdev_region(gpioled.devide, GPIOLED_CNT);
    return 0;
}

struct of_device_id led_of_match[]={
    {.compatible="alientek,gpioled"},
    {/**/},
};

struct platform_driver led_driver={
    .driver={
        .name="",
        .of_match_table=led_of_match,
    },
    .probe=led_probe,
    .remove=led_remove,
};
static int __init dtsleddriver_init(void){
    return platform_driver_register(&led_driver);
}

static void __exit dtsleddriver_exit(void){
    platform_driver_unregister(&led_driver);   
}

module_init(dtsleddriver_init);//加载模块
module_exit(dtsleddriver_exit);//注销模块
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liuchuanqiang");

程序说明:

  1. 在使用设备树进行platform驱动编写的时候,要将设备树下的compatible属性与驱动中的

struct platform_driver led_driver={

    .driver={

        .name="",

        .of_match_table=led_of_match,

    },

    .probe=led_probe,

    .remove=led_remove,

};

.of_match_table相匹配。led_of_match是个数组,里面写着设备树下的compatible属性内容。

struct of_device_id led_of_match[]={

    {.compatible="alientek,gpioled"},

    {/**/},

};

        2、在获取设备树节点时,不需要使用of_find_node_by_path等of函数,可直接读取platform_device结构体中的dev.of_node内容。这是因为设备树与platform驱动函数匹配以后,已经获得了节点内容。获取设备树节点gpioled.bl_nd1=dev->dev.of_node;,其中devplatform_device结构体变量。

设备树(device tree)机制是Linux内核从linux-3.x版本开始引进的一种机制,目的是解决内核源码的arch/arm目录下代码混乱的问题:随着ARM生态的快速发展,在内核源码的arch/arm目录下,存放着几十种arm芯片和几百个开发板相关的源文件,很多开发板和处理器的中断、寄存器等相关硬件资源都在这个目录下以.c或.h的文件格式定义。而对于内核来说,与这些硬件耦合,会导致内核代码混乱不堪,每个开发板上运行的内核镜像都必须单独编译配置,无法通用。什么时候Linux内核能像Windows镜像那样,无论你的电脑什么配置,一个Windows安装包,都可以直接下载安装运行呢?设备树机制,实现了Linux内核和硬件平台的解耦:每个硬件平台的硬件资源使用一个设备树文件(xxx.dts)来描述,而不是在arch/arm下以.c 或 .h 文件来定义。Linux内核是一个通用的内核,在启动过程中,在通过解析设备树中的硬件资源来初始化某个具体的平台。 引入设备树后,很多和内核驱动开发的工作也发生了变化:以往驱动工程师关注的头文件宏定义、寄存器定义,现在这些基本上不用关注,关注的重点则转向了如何根据硬件平台去配置和修改设备树文件。很多驱动的编程接口也发生了变化,开始慢慢使用device tree提供的编程接口去开发驱动。本期课程主要面向嵌入式开发人员,分享Linux驱动开发所需要的设备树知识和必备技能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值