要求:设备树点亮板子上的所有灯
测试代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
char buf[128] = "";
char ubuf[128] = {0};
int main(int argc, char const *argv[])
{
int fd = -1;
fd = open("/dev/myled0",O_RDWR);
if(-1 == fd)
{
perror("open is error");
exit(1);
}
while(1)
{
printf("请输入你想操控的位置>>0:内核板 1:拓展板\n");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0'; //去除换行
printf("请输入你想开灯还是关灯>>0:关灯 1:开灯\n");
fgets(buf+1,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0'; //去除换行
printf("请输入你想操控的灯>>1:LED1 2:LED2 3:LED3\n");
fgets(buf+2,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0'; //去除换行
printf("最后的操作码为:%s\n",buf);
write(fd,buf,sizeof(buf));
}
close(fd);
return 0;
}
驱动代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include<linux/of_gpio.h>
#include<linux/gpio.h>
#define CNAME "myled"
struct cdev *cdev;
struct class * cls;
struct device * dev;
#if 1
unsigned int major = 0;//动态申请设备号
#else
unsigned int major = 500; //静态指定设备号
#endif
int minor = 0;
const int count = 1;
char kbuf[128] = "";
//定义指针指向获取到的设备节点信息
struct device_node *nhb_node;
struct device_node *tzb_node;
//用于接收到的gpio编号
struct gpio_desc * tzb_gpiono1,* tzb_gpiono2,* tzb_gpiono3;
struct gpio_desc * nhb_gpiono1,* nhb_gpiono2,* nhb_gpiono3;
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
ssize_t mycdev_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff)
{
int ret;
printk("read:%s:%s:%d\n",__FILE__,__func__,__LINE__);
if(size > sizeof(kbuf)) size = sizeof(kbuf);
ret = copy_to_user(ubuf,kbuf,size);
if(ret)
{
printk("copy to user is error\n");
return -EIO;
}
return size;
return 0;
}
ssize_t mycdev_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{
int ret;
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
//校验传输数据大小,如果用户空间传输数据的大小大于内核空间,需要更正传输数据大小
if(size > sizeof(kbuf)) size = sizeof(kbuf);
ret = copy_from_user(kbuf,ubuf,size);
if(ret)
{
printk("copy from user is error\n");
return -EIO;
}
printk("传递过来的命令为:kbuf=%s\n",kbuf);
/*kbuf[0]:控制内核板还是拓展板
kbuf[1]:开灯还是关灯
kbuf[2]:具体的哪一盏灯
*/
switch(kbuf[0])
{
case '0'://内核
switch (kbuf[1])
{
case '0'://关灯
switch (kbuf[2])
{
case '1': //led1
gpiod_set_value(nhb_gpiono1,0);
break;
case '2': //led2
gpiod_set_value(nhb_gpiono2,0);
break;
case '3': //led3
gpiod_set_value(nhb_gpiono3,0);
break;
default:
break;
}
break;
case '1'://开灯
switch (kbuf[2])
{
case '1': //led1
gpiod_set_value(nhb_gpiono1,1);
break;
case '2': //led2
gpiod_set_value(nhb_gpiono2,1);
break;
case '3': //led3
gpiod_set_value(nhb_gpiono3,1);
break;
default:
break;
}
break;
default:
break;
}
case '1'://拓展板
switch (kbuf[1])
{
case '0'://关灯
switch (kbuf[2])
{
case '1': //led1
gpiod_set_value(tzb_gpiono1,0);
break;
case '2': //led2
gpiod_set_value(tzb_gpiono2,0);
break;
case '3': //led3
gpiod_set_value(tzb_gpiono3,0);
break;
default:
break;
}
break;
case '1'://开灯
switch (kbuf[2])
{
case '1': //led1
gpiod_set_value(tzb_gpiono1,1);
break;
case '2': //led2
gpiod_set_value(tzb_gpiono2,1);
break;
case '3': //led3
gpiod_set_value(tzb_gpiono3,1);
break;
default:
break;
}
break;
default:
break;
}
break;
}
return size;
}
int mycdev_close (struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
const struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
};
//入口
static int __init mycdev_init(void)
{
int ret;
int i;
dev_t devno;
//1.分配cdev结构体
cdev=cdev_alloc();
if(NULL==cdev)
{
printk("分配结构体失败\n");
ret = -EIO;
goto ERR1;
}
//2.初始化结构体
cdev_init(cdev,&fops);
//3.申请设备号
if(major>0)
{
//静态指定设备号
ret=register_chrdev_region(MKDEV(major,minor),count,CNAME);
if(ret)
{
printk("静态指定设备号失败\n");
ret= -ENOMEM;
goto ERR2;
}
}
else
{
//动态申请设备号
ret=alloc_chrdev_region(&devno,0,count,CNAME);
if(ret)
{
printk("动态指定设备号失败\n");
ret= -ENOMEM;
goto ERR2;
}
//获取主设备号
major=MAJOR(devno);
//获取次设备号
minor=MINOR(devno);
printk("动态指定设备号成功\n");
}
//4.驱动的注册
ret=cdev_add(cdev,MKDEV(major,minor),count);
if(ret)
{
printk("驱动注册失败\n");
ret=-EIO;
goto ERR3;
}
printk("驱动注册成功\n");
//5.自动创建设备节点
//1)向上层提交目录信息
cls=class_create(THIS_MODULE,CNAME);
if(IS_ERR(cls))
{
printk("向上层提交目录信息失败\n");
ret=PTR_ERR(cls);
goto ERR4;
}
//2)向上层提交设备节点信息
printk("向上层提交设备节点信息成功\n");
for(i=0;i<count;i++)
{
dev=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);
if(IS_ERR(dev))
{
printk("向上层提交设备节点信息失败\n");
ret=PTR_ERR(dev);
goto ERR5;
}
}
printk("向上层提交设备节点信息成功\n");
//灯的初始化
/*
nhb-leds{
led1=<&gpioz 5 0>;
led2=<&gpioz 6 0>;
led3=<&gpioz 7 0>;
};
tzb-leds{
led1=<&gpioe 10 0>;
led2=<&gpiof 10 0>;
led3=<&gpioe 8 0>;
}; */
//通过名字获取设备树结点信息
//拓展板
tzb_node=of_find_node_by_name(NULL,"tzb-leds");
if(tzb_node==NULL)
{
printk("通过名字解析设备树失败\n");
return -EFAULT;
}
printk("通过名字解析设备树成功\n");
//获取并申请gpio编号
tzb_gpiono1=gpiod_get_from_of_node(tzb_node,"led1",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(tzb_gpiono1))
{
printk("获取并申请gpio编号失败\n");
return PTR_ERR(tzb_gpiono1);
}
tzb_gpiono2=gpiod_get_from_of_node(tzb_node,"led2",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(tzb_gpiono2))
{
printk("获取并申请gpio编号失败\n");
return PTR_ERR(tzb_gpiono2);
}
tzb_gpiono3=gpiod_get_from_of_node(tzb_node,"led3",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(tzb_gpiono3))
{
printk("获取并申请gpio编号失败\n");
return PTR_ERR(tzb_gpiono3);
}
printk("获取gpio编号成功\n");
/*
//点亮拓展板led1
gpiod_set_value(tzb_gpiono1,1);
gpiod_set_value(tzb_gpiono2,1);
gpiod_set_value(tzb_gpiono3,1);
*/
/*****************************************************************************/
//内核板
nhb_node=of_find_node_by_name(NULL,"nhb-leds");
if(nhb_node==NULL)
{
printk("通过名字解析设备树失败\n");
return -EFAULT;
}
printk("通过名字解析设备树成功\n");
//获取并申请gpio编号
nhb_gpiono1=gpiod_get_from_of_node(nhb_node,"led1",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(nhb_gpiono1))
{
printk("获取并申请gpio编号失败\n");
return PTR_ERR(nhb_gpiono1);
}
nhb_gpiono2=gpiod_get_from_of_node(nhb_node,"led2",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(nhb_gpiono2))
{
printk("获取并申请gpio编号失败\n");
return PTR_ERR(nhb_gpiono2);
}
nhb_gpiono3=gpiod_get_from_of_node(nhb_node,"led3",0,GPIOD_OUT_LOW,NULL);
if(IS_ERR(nhb_gpiono3))
{
printk("获取并申请gpio编号失败\n");
return PTR_ERR(nhb_gpiono3);
}
printk("获取gpio编号成功\n");
/*
//点亮内核板led1
gpiod_set_value(nhb_gpiono1,1);
gpiod_set_value(nhb_gpiono2,1);
gpiod_set_value(nhb_gpiono3,1);
*/
return 0;
ERR5:
for(--i;i>0;i--)
{
device_destroy(cls,MKDEV(major,i));
}
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major,minor),count);
ERR2:
kfree(cdev);
ERR1:
return -EIO;
}
//出口
static void __exit mycdev_exit(void)
{
int i;
//1。销毁设备节点信息
for(i=0;i<count;i++)
{
device_destroy(cls,MKDEV(major,i));
}
//2.销毁目录信息
class_destroy(cls);
//3.驱动的注销
cdev_del(cdev);
//4.销毁设备号
unregister_chrdev_region(MKDEV(major,minor),count);
//5.释放cdev结构体
kfree(cdev);
//卸载驱动前熄灭灯
gpiod_set_value(tzb_gpiono1,0);
gpiod_set_value(tzb_gpiono2,0);
gpiod_set_value(tzb_gpiono3,0);
gpiod_set_value(nhb_gpiono1,0);
gpiod_set_value(nhb_gpiono2,0);
gpiod_set_value(nhb_gpiono3,0);
//释放申请得到的gpio编号
gpiod_put(tzb_gpiono1);
gpiod_put(tzb_gpiono2);
gpiod_put(tzb_gpiono3);
gpiod_put(nhb_gpiono1);
gpiod_put(nhb_gpiono2);
gpiod_put(nhb_gpiono3);
}
//指定入口地址
module_init(mycdev_init);
module_exit(mycdev_exit);
//指定出口地址
//许可证
MODULE_LICENSE("GPL");
设备树
现象: