要求
a.应用程序通过阻塞的io模型来读取number变量的值
b.number是内核驱动中的一个变量
c.number的值随着按键按下而改变(按键中断) 例如number=0 按下按键number=1 ,再次按下按键number=0
d.在按下按键的时候需要同时将led1的状态取反
e.驱动中需要编写字符设备驱动
f.驱动中需要自动创建设备节点
g.这个驱动需要的所有设备信息放在设备树的同一个节点中
pdrv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include<linux/workqueue.h>
#include<linux/timer.h>
/*
myplatform{
compatible="hqyj,platform";
reg=<0x12345678 0x14>; //填写内存地址
interrupt-parent =<&gpiof>; //填写中断
interrupts=<8 0>;
led2 = <&gpiof 10 0>;
};
*/
//用于挂载节点
struct cdev *cdev;
struct class *cls;
struct device *dev;
unsigned int count=3; //申请使用和挂载的节点数
#if 0
unsigned int major = 0;//动态申请
#else
unsigned int major = 500;//静态申请
#endif
int minor=0;//次设备号从0开始
//用于阻塞读
wait_queue_head_t wq_head; //定义阻塞队列头
unsigned int condition=0; //1用于判断是否有数据写入,唤醒阻塞
char kbuf[128]={}; //定义数组用于存放和用户之间拷贝的数据
unsigned int number=0; //0 led2亮 1 led2灭
//用于gpio 编号
struct gpio_desc *desc;
//接收软中断号
unsigned int irqno;
//工作队列对象
struct work_struct work;
//定时器对象
struct timer_list mytimer;
int flag=0;
//定时器处理函数:
void timer_handler(struct timer_list *timer)
{
if(flag==0)
{
++flag;
}
else
{
if(number==1)
printk("灭灯\n");
else if(number==0)
printk("亮灯\n");
}
}
//工作队列底半部处理函数
void work_func(struct work_struct *w)
{
//定时器耗时操作
mod_timer(&mytimer,jiffies+HZ);
}
//中断处理函数
irqreturn_t irq_handler(int irq, void *dev)
{
//修改number
number=!number;
memset(kbuf,0,sizeof(kbuf));
kbuf[0]=number;
condition=1;
wake_up_interruptible(&wq_head);
//亮灭灯
gpiod_set_value(desc,!gpiod_get_value(desc));
//启用中断底半部
schedule_work(&work);
return IRQ_HANDLED;
}
//read
ssize_t mycdev_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff)
{
int ret; //size参数是用户期待读到的字节长度
if(file->f_flags&O_NONBLOCK)
{
//非阻塞
return -EINVAL;
}
else{
//阻塞
ret=wait_event_interruptible(wq_head,condition);
if(ret)
{
printk("接收阻塞失败\n");
return -ERESTARTSYS;
}
// printk("接收阻塞休眠\n");
if(size<sizeof(kbuf))
size=sizeof(kbuf);
ret=copy_to_user(ubuf,kbuf,size);
if(ret)
{
printk("数据从内核拷贝给用户失败\n");
return -EIO;
}
}
//condition 重新赋值为假
condition=0;
return size;
}
//操作方法结构体的初始化
struct file_operations fops={
.read=mycdev_read,
};
int pdrv_probe(struct platform_device *pdr)
{
int ret;
//中断————————————————————————————————————————————————————————————
//获取中断号
irqno=platform_get_irq(pdr,0);
if(irqno<0)
{
printk("获取中断号失败");
return irqno;
}
//注册中断
ret=request_irq(irqno,irq_handler,IRQF_TRIGGER_FALLING,NULL,NULL);
if(ret)
{
printk("注册中断失败\n");
return ret;
}
printk("注册中断成功\n");
//获取GPIO编号
desc=gpiod_get_from_of_node(pdr->dev.of_node,"led2",0,GPIOD_OUT_HIGH,NULL);
if(IS_ERR(desc))
{
printk("获取GPIO编号失败\n");
return PTR_ERR(desc);
}
printk("获取GPIO编号成功\n");
return 0;
}
int pdrv_remove(struct platform_device *pdr)
{
printk("%s:%d\n",__FILE__,__LINE__);
return 0;
}
//定义compatible表 通过设备树获取信息
struct of_device_id oftable[]={
{.compatible="hqyj,platform"},
{},
};
MODULE_DEVICE_TABLE(of,oftable);
//对象初始化
struct platform_driver pdrv ={
.probe=pdrv_probe,
.remove=pdrv_remove,
.driver={
.name= "test",
.of_match_table=oftable, //设备树匹配
},
//.id_table=idtable, //通过名字表获取信息
};
static int __init mycdev_init(void)
{
int ret,i;
dev_t devno;
//工作队列初始化
INIT_WORK(&work, work_func);
//用于挂载节点-----分步
//申请驱动设备结构体空间
cdev=cdev_alloc();
if(cdev==NULL)
{
printk("cdev alloc memory err\n");
ret = -ENOMEM;
goto ERR1;
}
printk("对象分配成功\n");
//对象的初始化
cdev_init(cdev,&fops);
//设备号的申请
if(major==0)//动态申请
{
ret=alloc_chrdev_region(&devno,minor,count,"my_home");
if(ret)
{
printk("动态申请设备号失败\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
printk("动态申请设备号成功\n");
}
else
{
ret=register_chrdev_region(MKDEV(major,minor),count,"my_pdrv");
if(ret)
{
printk("静态申请设备号失败\n");
goto ERR2;
}
printk("静态申请设备号成功\n");
}
ret=cdev_add(cdev,MKDEV(major,minor),count);
if(ret)
{
printk("字符设备驱动注册失败\n");
goto ERR3;
}
printk("注册字符设备驱动成功\n");
//自动创建设备节点
cls = class_create(THIS_MODULE,"home");
if(IS_ERR(cls))
{
printk("创建逻辑节点目录失败\n");
ret=PTR_ERR(cls);
goto ERR4;
}
printk("创建逻辑节点目录成功\n");
//向上提交节点信息
for(i=0;i<count;i++)
{
dev = device_create(cls,NULL,MKDEV(major,i),NULL,"my_led%d",i+1);
if(IS_ERR(dev))
{
printk("创建逻辑节点失败\n");
ret = PTR_ERR(dev);
goto ERR5;
}
}
printk("创建逻辑节点成功\n");
//初始化队列头
init_waitqueue_head(&wq_head);
//注册对象
platform_driver_register(&pdrv);
//初始化定时器
mytimer.expires=jiffies+HZ;//定时1s
timer_setup(&mytimer,timer_handler,0);
//启动定时器
add_timer(&mytimer);
return 0;
ERR5:
for(--i;i>=0;i--)
{
device_destroy(cls,MKDEV(major,i));
}
class_destroy(cls);
ERR4:
cdev_del(cdev);
ERR3:
unregister_chrdev_region(MKDEV(major,minor),count);
ERR2:
kfree(cdev);
ERR1:
return ret;
}
static void __exit mycdev_exit(void)
{
int i;
//1.销毁设备节点
for(i=0;i<count;i++)
{
device_destroy(cls,MKDEV(major,i));
}
class_destroy(cls);
//2.注销字符设备驱动
cdev_del(cdev);
//3.释放设备号
unregister_chrdev_region(MKDEV(major,minor),count);
//4.释放动态申请的空间
kfree(cdev);
//注销中断
free_irq(irqno,NULL);
//删除定时器对象
del_timer(&mytimer);
//释放gpio编号
gpiod_set_value(desc,0);
gpiod_put(desc);
//注销对象
platform_driver_unregister(&pdrv);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("cjy_sh@hqyj.com假的");//作者联系方式
test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/time.h>
int main(int argc, char const *argv[])
{
int fd;
int number;
char buf[128] = {};
fd = open("/dev/my_led2", O_RDWR);
if (fd < 0)
{
printf("打开文件失败\n");
exit(-1);
}
while (1)
{
read(fd, buf, sizeof(buf));
printf("number = %d\n", buf[0]);
memset(buf,0,sizeof(buf));
}
close(fd);
return 0;
}