ZYNQ7020 linux下platform框架的驱动led

#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>

#define MYLED_CNT	1		//设备号个数
#define MYLED_NAME	"myled"	//名字

//LED设备结构体
struct myled_dev{
	dev_t devid;			//设备号
	struct cdev cdev;		//cdev结构体
	struct class *class;	//类
	struct device *device;	//设备号
	int led_gpio;			//GPIO编号
};

static struct myled_dev myled;	//LED设备

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

static ssize_t myled_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int ret;
	char kern_buf[1];
	
	ret = copy_from_user(kern_buf, buf, cnt);	//得到应用层传过来的数据
	if(ret > 0)
	{
		printk("copy form user failed\n");
		return -EFAULT;
	}
	
	if(kern_buf[0] == 0)
	{
		gpio_set_value(myled.led_gpio, 0);
	}
	else if(kern_buf[0] == 1)
	{
		gpio_set_value(myled.led_gpio, 1);
	}
	
	return 0;
}

static int myled_init(struct device_node *nd)
{
	const char *str;
	int val;
	int ret;
	//使用 of_get_named_gpio 函数获取设备树中 led 节点指定的gpio 管脚,得到一个 GPIO 编号,
	//那么这个编号是 linux 内核 GPIO 子系统对 gpio 的编号,一个编号就对应一个 gpio,
	//那么我们这里得到的这个编号就对应设备树中 led-gpio 指定的那个管脚。
	
	//参数1:设备节点	参数2:要获取的GPIO信息的属性名	
	//参数3:GPIO索引,因为一个属性里面可能包含多个GPIO,此参数指定要获取哪个GPIO的编号,如果只有一个GPIO信息,此参数为0
	//返回值:获取到的GPIO编号,为负值时,获取失败
	myled.led_gpio = of_get_named_gpio(nd, "led-gpio", 0);
	
	//获得GPIO编号后,使用该函数判断编号是否有效
	if(!gpio_is_valid(myled.led_gpio))
	{
		printk("of_get_named_gpio is failed\n");
		return -EINVAL;
	}
	
	//申请使用GPIO,参数1:gpio编号,参数2:给gpio设置名字
	//返回值:0成功,其他失败
	ret = gpio_request(myled.led_gpio, "pl_led");
	if(ret)
	{
		printk("gpio_request is failed\n");
		return ret;
	}
	
	//用于读取属性中字符串的值,参数1:设备节点, 参数2:要读取的属性名字
	//参数3:读取到的字符串 返回值:0成功,负值失败
	ret = of_property_read_string(nd, "default-state", &str);
	if(!ret)
	{
		if(!strcmp(str, "on"))
		{
			val = 1;
		}
		else
		{
			val = 0;
		}
	}
	else
	{
		val = 0;
	}
	
	//将gpio设置为输出模式,并设置GPIO初始电平状态
	gpio_direction_output(myled.led_gpio, val);
	
	return 0;
}

//LED设备操作函数
static struct file_operations myled_fops = {
	.owner = THIS_MODULE,
	.open = myled_open,
	.write = myled_write,
};

//platform驱动的probe函数,当驱动与设备匹配成功后此函数就会执行
//参数1:platform设备指针,返回值:0成功, 其他负值失败
static int myled_probe(struct platform_device *pdev)
{
	int ret;
	printk("driver and device has matched\n");
	
	//led初始化
	ret = myled_init(pdev->dev.of_node);
	if(ret)
		return ret;
	
	//注册字符设备驱动
	//动态分配设备号	参数1:保存申请到的设备号,
	//参数2:次设备号起始地址,以该地址连续申请多个次设备号,一般从0开始
	//参数3:要申请的设备号数量		参数4:设备名字
	//返回值:成功返回0,失败返回负数
	ret = alloc_chrdev_region(&myled.devid, 0, MYLED_CNT, MYLED_NAME);
	if(ret)
		goto out1;
	
	//初始化cdev
	myled.cdev.owner = THIS_MODULE;
	cdev_init(&myled.cdev, &myled_fops);
	
	//向linux系统添加字符设备cdev
	ret = cdev_add(&myled.cdev, myled.devid, MYLED_CNT);
	if(ret)
		goto out2;
	
	//自动创建设备节点的工作是在驱动程序的入口函数中完成的
	//一般在cdev_add函数后面添加自动创建设备节点相关代码
	//首先需要创建类class,参数1:一般为THIS_MODULE,参数2:类的名字
	//返回值:指向结构体class的指针
	myled.class = class_create(THIS_MODULE, MYLED_NAME);
	if(IS_ERR(myled.class))
	{
		ret = PTR_ERR(myled.class);
		goto out3;
	}
	//创建好类之后还不能实现自动创建设备节点,还需要在这个类下创建一个设备
	//参数1:设备要创建在哪个类下
	//参数2:为父设备,一般为NULL
	//参数3:设备号
	//参数4:为设备可能会用到的数据,一般为NULL
	//参数5:设备名字,在/dev/设备名字
	myled.device = device_create(myled.class, &pdev->dev, myled.devid, NULL, MYLED_NAME);
	if(IS_ERR(myled.device))
	{
		ret = PTR_ERR(myled.device);
		goto out4;
	}
	
	return 0;

//设备创建失败,需要删除掉类(上面创建了类)
out4:
	class_destroy(myled.class);

//创建类class失败,需要删除相应的字符设备(上面添加了字符设备)
out3:
	cdev_del(&myled.cdev);

//向linux系统添加字符设备cdev失败,释放掉设备号(上面动态分配了设备号)
out2:
	unregister_chrdev_region(myled.devid, MYLED_CNT);

//动态分配设备号失败,释放gpio
out1:
	//不使用某个GPIO时,调用gpio_free释放gpio
	gpio_free(myled.led_gpio);
	
	return ret;
}

//platform驱动模块卸载时,此函数会执行
static int myled_remove(struct platform_device *dev)
{
	printk("led platform driver remove\n");
	
	//删除创建的设备
	device_destroy(myled.class, myled.devid);
	
	//删除类
	class_destroy(myled.class);
	
	//删除字符设备
	cdev_del(&myled.cdev);
	
	//释放掉设备号
	unregister_chrdev_region(myled.devid, MYLED_CNT);
	
	//释放gpio
	gpio_free(myled.led_gpio);

    return 0;
}

//匹配列表
static const struct of_device_id led_of_match[] = {
	{.compatible = "lnb, led"},
	{}
};

//platform驱动结构体
static struct platform_driver myled_driver = {
	.driver = {
		.name 			= "zynq-led",	//驱动名字,用于和设备匹配
		.of_match_table = led_of_match,	//设备树匹配表,用于和设备树中定义的设备匹配
	},
	.probe	=	myled_probe,	//probe函数
	.remove	=	myled_remove,	//remove函数
};

//模块入口函数
static int __init myled_driver_init(void)
{
	return platform_driver_register(&myled_driver);
}

//模块出口函数
static void __exit myled_driver_exit(void)
{
	platform_driver_unregister(&myled_driver);
}

module_init(myled_driver_init);
module_exit(myled_driver_exit);

MODULE_AUTHOR("wnyears");
MODULE_DESCRIPTION("Platform Driver");
MODULE_LICENSE("GPL");

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值