.c 文件
#include <linux/init.h>
#include <linux/module.h>
#include<linux/of.h>
#include<linux/of_gpio.h>
#include<linux/gpio.h>
#include <linux/cdev.h>
#define N 1 //节点个数
int mycdev_open(struct inode *inode, struct file *file); //对应的是open()
ssize_t mycdev_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff); //read()
ssize_t mycdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff); //write()
int mycdev_close(struct inode *inode, struct file *file); //close()
int led_init(void);
//操作方法结构体的初始化
struct file_operations fops={
.open=mycdev_open,
.read=mycdev_read,
.write=mycdev_write,
.release=mycdev_close,
};
char kbuf[128]={}; //定义数组用于存放和用户之间拷贝的数据
struct class *cls; //句柄
struct device *dev;
struct cdev *cdev; //1.分配字符设备对象
dev_t devno=300; //静态指定设备号
int i,ret;
struct device_node* prent_node;
struct device_node* child_node;
int gpiono1,gpiono2,gpiono3;
//入口函数,当驱动安装的时候执行
static int __init demo_init(void)
{
//2.给字符设备对象申请空间
cdev = cdev_alloc();
if(cdev==NULL)
{
printk("设备对象申请空间失败\n");
goto ERR1;
}
printk("设备对象申请空间成功\n");
//3.初始化设备驱动对象(部分)
cdev_init(cdev, &fops);
//4.动态指定设备号
if(alloc_chrdev_region(&devno, 0, N, "my_led"))
{
printk("动态申请设备号失败\n");
goto ERR2;
}
printk("动态指定设备号成功 \n");
//5.注册设备驱动
if(cdev_add(cdev, devno, N))
{
printk("注册驱动失败\n");
goto ERR3;
return -1;
}
printk("注册驱动成功\n");
//6.自动创建节点
//向上提交节点目录
cls = class_create(THIS_MODULE,"MYLED");
if(IS_ERR(cls))
{
printk("向上提交目录失败\n");
goto ERR4;
}
printk("向上提交目录成功\n");
for(i=0; i<N; i++)
{
//创建设备节点
dev = device_create(cls,NULL,MKDEV(MAJOR(devno),i),NULL,"led%d",i);
if(IS_ERR(dev))
{
printk("创建节点失败\n");
goto ERR5;
}
printk("创建节点%d成功\n",MKDEV(MAJOR(devno),i));
}
led_init(); //led灯的初始化
return 0;
ERR5:
//销毁节点
for(i-=N; i>=0;i--)
device_destroy(cls,MKDEV(MAJOR(devno),i));
//销毁目录
class_destroy(cls);
ERR4:
//注销字符设备驱动
cdev_del(cdev);
ERR3:
//注销设备号
unregister_chrdev_region(devno, 3);
ERR2:
//注销设备对象
kfree(cdev);
ERR1:
return -1;
}
//出口函数,卸载驱动的时候执行
static void __exit demo_exit(void)
{
gpio_set_value(gpiono1,0);
gpio_set_value(gpiono2,0);
gpio_set_value(gpiono3,0);
gpio_free(gpiono1);
gpio_free(gpiono2);
gpio_free(gpiono3);
//注销设备节点
device_destroy(cls,MKDEV(MAJOR(devno),0));
//销毁目录
class_destroy(cls);
printk("销毁目录成功\n");
//注销字符设备驱动
cdev_del(cdev);
printk("销毁字符驱动成功\n");
//注销设备号
unregister_chrdev_region(devno, 1);
printk("销毁设备号成功\n");
//注销设备驱动对象
kfree(cdev);
printk("销毁驱动对象成功\n");
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
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)
{
//size参数是用户期待读到的字节长度
if(size>sizeof(kbuf))
size=sizeof(kbuf);
ret=copy_to_user(ubuf,kbuf,size);
if(ret)
{
printk("数据从内核向用户拷贝失败\n");
return -EIO;
}
return size;
}
ssize_t mycdev_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{
if(size>sizeof(kbuf))
size=sizeof(kbuf);
ret=copy_from_user(kbuf,ubuf,size);
if(ret)
{
printk("数据从内核向用户拷贝失败\n");
return -EIO;
}
printk("内核接收到的数据是:%s\n",kbuf);
switch (kbuf[0])
{
case '1'://亮
switch (kbuf[1])
{
case '1':
gpio_set_value(gpiono1,1);
break;
case '2':
gpio_set_value(gpiono2,1);
break;
case '3':
gpio_set_value(gpiono3,1);
break;
default:
break;
}
break;
case '0'://灭
switch (kbuf[1])
{
case '1':
gpio_set_value(gpiono1,0);
break;
case '2':
gpio_set_value(gpiono2,0);
break;
case '3':
gpio_set_value(gpiono3,0);
break;
default:
break;
}
break;
default:
break;
}
return size;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
return 0;
}
int led_init(void)
{
prent_node = of_find_node_by_path("/myleds");
if(NULL==prent_node)
{
printk("获取父节点信息失败\n");
return -1;
}
child_node = of_get_child_by_name(prent_node,"extend_leds");
if(NULL==child_node)
{
printk("获取子节点信息失败\n");
return -1;
}
gpiono1 = of_get_named_gpio(child_node, "led1", 0);
if(gpiono1<0)
{
printk("申请LED1GPIO编号失败\n");
return -1;
}
gpiono2 = of_get_named_gpio(child_node, "led2", 0);
if(gpiono1<0)
{
printk("申请LED1GPIO编号失败\n");
return -1;
}
gpiono3 = of_get_named_gpio(child_node, "led3", 0);
if(gpiono1<0)
{
printk("申请LED1GPIO编号失败\n");
return -1;
}
if(gpio_request(gpiono1,NULL))
{
printk("LED1使用权限申请失败\n");
return -1;
}
if(gpio_request(gpiono2,NULL))
{
printk("LED1使用权限申请失败\n");
return -1;
}
if(gpio_request(gpiono3,NULL))
{
printk("LED1使用权限申请失败\n");
return -1;
}
//方向设置为输出方向
gpio_direction_output(gpiono1, 0);
gpio_direction_output(gpiono2, 0);
gpio_direction_output(gpiono3, 0);
//初始电平为低电平
gpio_set_value(gpiono1,0);
gpio_set_value(gpiono2,0);
gpio_set_value(gpiono3,0);
return 0;
}
测试文件 .c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
//打开节点
int fd = open("/dev/led0",O_RDWR);
if(fd<0)
{
printf("打开设备节点失败\n");
return -1;
}
char contrl[2];//第一个字符表示亮灭,第二个字符表示操控那一盏灯
while(1)
{
printf("请输入控制方式: 1:开 0:关\n");
contrl[0] = getchar();
while (getchar()!='\n');
if(contrl[0]=='1')//开灯
{
printf("请输入要开那盏灯: 1:LED1 2:LED2 3:LED3\n");
contrl[1] = getchar();
while (getchar()!='\n');
write(fd, contrl, 2);
}else//关灯
{
printf("请输入要关那盏灯: 1:LED1 2:LED2 3:LED3\n");
contrl[1] = getchar();
while (getchar()!='\n');
write(fd, contrl, 2);
}
}
close(fd);
return 0;
}
设备树添加的节点:
leds{
extend-leds{
led1=<&gpioe 10 0>;
led2=<&gpiof 10 0>;
led3=<&gpioe 8 0>;
};
core-leds{
led1=<&gpioz 5 0>;
led2=<&gpioz 6 0>;
led3=<&gpioz 7 0>;
};
};
测试结果: