本文章给出使用设备树来驱动板卡上的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就可以控制灯了。