(六)使用platform框架来驱动led(本地编写设备树方式)

一、采用设备树方式来驱动

上一节采用platform框架来驱动led,不过本地仍然自己写device设备信息,但是我们这边采用设备树,而设备树也可以记录设备的信息,所以建议使用设备树来配合platform框架来写。

1.2、设备树修改

设备树请参考前面我写的 pinctrl控制系统驱动led 这篇文章。

1.3、driver文件

下面是基于设备树来写的驱动,其中大部分和pinctrl控制驱动led类似。

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>

#define PLATTREE_NAME   "plattree"
#define PLATTREE_CT     1

#define LED_ON  1
#define LED_OFF 0

struct plattree_struct{
    int major;
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;
    int gpio_num;

};

struct plattree_struct plattree_dev;


static int plattree_open(struct inode *inode, struct file *file)
{
    file->private_data = &plattree_dev;
    return 0;
}

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


ssize_t plattree_write(struct file *file, const char __user *data,size_t len, loff_t *ppos)
{
    unsigned char data_tmp = 0;
    int ret = 0;
    struct plattree_struct *dev = file->private_data;

    ret = copy_from_user(&data_tmp,data,1);
    if(ret < 0){
        printk("write err\r\n");
        return -1;
    }

    if(data_tmp == LED_ON){
        gpio_set_value(dev->gpio_num,0);
    }
    else if(data_tmp == LED_OFF){
        gpio_set_value(dev->gpio_num,1);
    }

    return 0;
}

ssize_t plattree_read(struct file *file, char __user * buf,size_t len, loff_t * ppos)
{
    return 0;
}

static const struct file_operations plattree_ops = {
    .owner      = THIS_MODULE,
    .open       = plattree_open,
    .write      = plattree_write,
    .read       = plattree_read,
    .release    = plattree_release,
};

int led_init(void)
{
    int ret = 0;
    plattree_dev.nd = of_find_node_by_path("/gpioled");
    if(plattree_dev.nd == NULL){
        printk("not find node\r\n");
        return -1;
    }

    printk("find node\r\n");

    plattree_dev.gpio_num = of_get_named_gpio(plattree_dev.nd,"led-gpio",0);
    if(plattree_dev.gpio_num < 0){
        printk("not find gpio\r\n");
        return -2;
    }

    printk("gpio num:%d\r\n",plattree_dev.gpio_num);

    ret = gpio_request(plattree_dev.gpio_num,"led0");
    if(ret < 0){
        printk("gpio already use\r\n");
        return -3;
    }

    ret = gpio_direction_output(plattree_dev.gpio_num,1);
    if(ret < 0){
        printk("gpio set err\r\n");
        return -4;
    }

    return 0;
}

static int plattree_probe(struct platform_device *dev)
{
    int ret = 0;

    ret = led_init();
    if(ret < 0){
        goto chrdev_err;
    }

    plattree_dev.major = 0;

    if(plattree_dev.major){
        plattree_dev.devid = MKDEV(plattree_dev.major,0);
        ret = register_chrdev_region(plattree_dev.devid,PLATTREE_CT,PLATTREE_NAME);
    }
    else{
        ret = alloc_chrdev_region(&plattree_dev.devid,0,PLATTREE_CT,PLATTREE_NAME);
        plattree_dev.major = MAJOR(plattree_dev.devid);
    }

    if(ret < 0){
        printk("chrdev err\r\n");
        goto chrdev_err;
    }

    printk("major num:%#x",plattree_dev.major);

    plattree_dev.cdev.owner = THIS_MODULE;

    cdev_init(&plattree_dev.cdev,&plattree_ops);

    ret = cdev_add(&plattree_dev.cdev,plattree_dev.devid,PLATTREE_CT);

    if(ret < 0){
        printk("cdev err\r\n");
        goto cdev_err;
    }

    plattree_dev.class = class_create(THIS_MODULE,PLATTREE_NAME);

    if(IS_ERR(plattree_dev.class)){
        ret = PTR_ERR(plattree_dev.class);
        goto class_err;
    }

    plattree_dev.device = device_create(plattree_dev.class,NULL,plattree_dev.devid,NULL,PLATTREE_NAME);

    if(IS_ERR(plattree_dev.device)){
        ret = PTR_ERR(plattree_dev.device);
        goto device_err;
    }

    return 0;

device_err:
    class_destroy(plattree_dev.class);

class_err:
    cdev_del(&plattree_dev.cdev);

cdev_err:
    unregister_chrdev_region(plattree_dev.devid,PLATTREE_CT);

chrdev_err:
    return ret;
}

static int plattree_remove(struct platform_device *dev)
{
    device_destroy(plattree_dev.class,plattree_dev.devid);
    class_destroy(plattree_dev.class);
    cdev_del(&plattree_dev.cdev);
    unregister_chrdev_region(plattree_dev.devid,PLATTREE_CT);
    return 0;
}
static const struct of_device_id led_of_match[] = {
    {.compatible    = "atkalpha-gpioled"},
    {},
};

static struct platform_driver plattree_driver = {
    .driver = {
        .name   = "imx6ull-led",
        .of_match_table = led_of_match,
    },
    .probe  = plattree_probe,
    .remove = plattree_remove,
};

static int __init plattree_init(void)
{
    return platform_driver_register(&plattree_driver);
}

static void __exit plattreee_exit(void)
{
    platform_driver_unregister(&plattree_driver);
}

module_init(plattree_init);
module_exit(plattreee_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("gale");

其中下面这个至关重要:

static const struct of_device_id led_of_match[] = {
    {.compatible    = "atkalpha-gpioled"},
    {},
};

该段就是定义compatible这个属性的内容,该内容必须和设备书中的内容一致,否则驱动将无法匹配设备,也就不会执行该函数plattree_probe,那么驱动将会加载失败。
以下是设备树中对应属性的值。

gpioled{
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "atkalpha-gpioled";
    	pinctrl-name = "default";
		pinctrl-0=<&pinctrl_led>;
		led-gpio = 	< &gpio1 3 GPIO_ACTIVE_LOW >;	//低电平点亮 
		status = "okay";
	};

其实关于驱动和设备匹配有四种方法,第一种就是name属性匹配,第二种就是利用设备树中的compatible匹配,第三种和第四种目前我还没有尝试过,暂时不讨论。

发布了29 篇原创文章 · 获赞 0 · 访问量 429

猜你喜欢

转载自blog.csdn.net/weixin_42547950/article/details/104208697
今日推荐