一、采用设备树方式来驱动
上一节采用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匹配,第三种和第四种目前我还没有尝试过,暂时不讨论。