linux驱动-设备树简单使用

 本文章给出使用设备树来驱动板卡上的led灯的示例。设备树对gpio的描述如下:

/ {
    gpio-leds{
        compatible = "gpio-leds";
        
        led1{
            label = "led1"; 
            gpios = <&tegra_main_gpio TEGRA_MAIN_GPIO(R, 5) GPIO_ACTIVE_HIGH>;
        };
        
        led2{
            label = "led2"; 
            gpios = <&tegra_main_gpio TEGRA_MAIN_GPIO(R, 1) GPIO_ACTIVE_HIGH>;
        };
    };
};

   驱动代码如下:

   dt_test.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "dt_test.h"

struct gpio_led_data{
	const char *desc;
	int gpio;
	int active_high;
};

struct gpio_leds_platform_data{
	struct gpio_led_data *leds;
	int nleds;
};

static const struct of_device_id dt_test_of_match[] = {
	{ .compatible = "gpio-leds", },
	{ },
};

dev_t gpio_led_dev_t;
struct cdev *dt_test_dev;
struct class *myclass;
struct 	gpio_leds_platform_data *pdata;

static struct gpio_leds_platform_data *
gpio_leds_get_devtree_pdata(struct device *dev)
{
	struct device_node *node,*pp;
	struct 	gpio_leds_platform_data *pdata;
	struct gpio_led_data *led;
	int nleds;
	int i;

	node = dev->of_node;
	if (!node)
		return ERR_PTR(-ENODEV);

	nleds = of_get_child_count(node);
	if (nleds == 0)
		return ERR_PTR(-ENODEV);

	pdata = devm_kzalloc(dev,
			     sizeof(*pdata) + nleds * sizeof(*led),
			     GFP_KERNEL);
	if (!pdata)
		return ERR_PTR(-ENOMEM);

	pdata->leds = (struct gpio_keys_button *)(pdata + 1);
	pdata->nleds = nleds;

	i = 0;
	for_each_child_of_node(node, pp){
		enum of_gpio_flags flags;
		led = (struct gpio_led_data *)&pdata->leds[i++];
		led->desc = of_get_property(pp, "label", NULL);
		led->gpio = of_get_gpio_flags(pp, 0, &flags);
		led->active_high = flags & OF_GPIO_ACTIVE_LOW;
		gpio_set_value(led->gpio,led->active_high);
	}

	return pdata;
}


static int gpio_leds_open(struct inode *inode, struct file *file)
{
	return 0;
}
static long gpio_leds_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct gpio_led_data *led;
	int i;
	void __user *argp = (void __user *)arg;
	char __user *p = argp;
	switch(cmd){
		case DT_LED_IOCTL_LED_ON:
			for(i = 0; i < pdata->nleds; i++){
				led = &pdata->leds[i];
				if(!strcmp(p,led->desc)){
					gpio_set_value(led->gpio,!led->active_high);
					break;
				}
			}
			break;
		case DT_LED_IOCTL_LED_OFF:
			for(i = 0; i < pdata->nleds; i++){
				led = &pdata->leds[i];
				if(!strcmp(p,led->desc)){
					gpio_set_value(led->gpio,led->active_high);
					break;
				}
			}
			break;
		default:
			break;
	}
	
	return 0;
}

static const struct file_operations gpio_leds_fops = {
	.owner		= THIS_MODULE,
	.open		= gpio_leds_open,
	.unlocked_ioctl = gpio_leds_ioctl,
};

static int dt_test_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct device *mydevice;
	int ret;
	pdata = gpio_leds_get_devtree_pdata(dev);
	if (IS_ERR(pdata))
		return PTR_ERR(pdata);

	dt_test_dev = cdev_alloc();
	if (IS_ERR(dt_test_dev))
		return PTR_ERR(dt_test_dev);
	cdev_init(dt_test_dev,&gpio_leds_fops);
	ret = alloc_chrdev_region(&gpio_led_dev_t,0,pdata->nleds,"gpio_leds");
	if(ret)
		goto fail3;
	ret = cdev_add(dt_test_dev,gpio_led_dev_t,pdata->nleds);
	if(ret)
		goto fail2;
	myclass = class_create(THIS_MODULE, "gpio_leds");
	if (IS_ERR(myclass)) 
		goto fail1;
	mydevice = device_create(myclass,NULL,gpio_led_dev_t,NULL,"gpio_leds");
	if (IS_ERR(myclass)) 
		goto fail;

	return 0;
fail:
	class_destroy(myclass);
fail1:
	cdev_del(dt_test_dev);

fail2:
	unregister_chrdev_region(gpio_led_dev_t,pdata->nleds);
fail3:
	return -1;
	
}

static int dt_test_remove(struct platform_device *pdev)
{
	device_destroy(myclass,gpio_led_dev_t);
	class_destroy(myclass);
	cdev_del(dt_test_dev);

	unregister_chrdev_region(gpio_led_dev_t,pdata->nleds);
	return 0;	
}

static struct platform_driver dt_test_device_driver = {
	.probe		= dt_test_probe,
	.remove		= dt_test_remove,
	.driver		= {
		.name	= "dt_test",
		.of_match_table = of_match_ptr(dt_test_of_match),
	}
};

static int __init dt_test_init(void)
{
	return platform_driver_register(&dt_test_device_driver);
}

static void __exit dt_test_exit(void)
{
	platform_driver_unregister(&dt_test_device_driver);
}

module_init(dt_test_init);
module_exit(dt_test_exit);

MODULE_LICENSE("GPL");

 dt_test.h

#ifndef _DT_TEST_H
#define _DT_TEST_H

#include 
#include 

#define	DT_LED_IOCTL_BASE	'D'

#define	DT_LED_IOCTL_LED_ON		_IOW(DT_LED_IOCTL_BASE, 0, int)
#define	DT_LED_IOCTL_LED_OFF	    _IOW(DT_LED_IOCTL_BASE, 1, int)


#endif /*_DT_TEST_H*/

    整体来说和没有设备树的驱动区别在板载资源匹配这一块,对于设备树的解析内核提供了一系列的接口比如of_get_propertyof_get_property等。其他方面和我们之前使用设备文件来编写驱动是一样的。对于设备树的语法格式,这里就不做描述了,不清楚的可以去网上查找资料。然后驱动加载后,打开设备使用ioctl就可以控制灯了。

你可能感兴趣的:(linux驱动)