led.c
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/timer.h>
#include <linux/uaccess.h>
#include<linux/slab.h>
#include<linux/fs.h>
#include<linux/device.h>
int condition=0;//数据准备状态
int number=0;
struct cdev* cdev;//字符设备驱动对象
struct device *dev;//设备信息
struct class *cls;//目录信息
char kbuf[128]={0};
dev_t devno;
unsigned int major=0;//主设备
unsigned int minor=0;//次设备
wait_queue_head_t wq_head;//等待头节点
struct device_node *dnode;
struct resource *res;
unsigned int irqno;
struct gpio_desc *gpiono;
//中断处理
irqreturn_t myirq_handler(int irqno ,void *dev_id)
{
number=!number;
gpiod_set_value(gpiono,!gpiod_get_value(gpiono));
condition=1;//数据就绪
wake_up_interruptible(&wq_head);//唤醒休眠进程
return IRQ_HANDLED;
}
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 *lof)
{
int ret;
if(sizeof(number)<size)
size=sizeof(number);//给最大空间
wait_event_interruptible(wq_head,condition);//将进程切换成休眠
ret=copy_to_user(ubuf,&number,size);
if(ret)
{
printk("copy_to_user file \n");
return -EIO;
}
printk("%s %s %d\n",__FILE__,__func__,__LINE__);
condition=0;//表示下一次硬件数据没有准备好
return 0;
}
int mycdev_close (struct inode *inode, struct file *file)
{
printk("%s %s %d\n",__FILE__,__func__,__LINE__);
return 0;
}
struct file_operations fops=
{
.open=mycdev_open,
.read=mycdev_read,
.release=mycdev_close,
};
// probe函数,匹配设备成功执行
int pdrv_probe(struct platform_device *pdev)
{
int ret, i;
// 初始化等待队列头
init_waitqueue_head(&wq_head);
// 1.分配字符设备驱动对象空间 cdev_alloc
cdev = cdev_alloc();
if (cdev == NULL)
{
printk("申请字符设备驱动对象空间失败\n");
ret = -EFAULT;
goto out1;
}
printk("字符设备驱动对象申请成功\n");
// 2.字符设备驱动对象部分初始化 cdev_init
cdev_init(cdev, &fops);
// 3.申请设备号 register_chrdev_region/alloc_chrdev_region
if (major > 0) // 静态申请设备号
{
ret = register_chrdev_region(MKDEV(major, minor), 3, "myled");
if (ret)
{
printk("静态指定设备号失败\n");
goto out2;
}
}
else // 动态申请设备号
{
ret = alloc_chrdev_region(&devno, minor, 3, "myled");
if (ret)
{
printk("动态申请设备号失败\n");
goto out2;
}
major = MAJOR(devno); // 根据设备号得到主设备号
minor = MINOR(devno); // 根据设备号得到次设备号
}
printk("申请设备号成功\n");
// 4.注册字符设备驱动对象 cdev_add()
ret = cdev_add(cdev, MKDEV(major, minor), 3);
if (ret)
{
printk("注册字符设备驱动对象失败\n");
goto out3;
}
printk("注册字符设备驱动对象成功\n");
// 5.向上提交目录
cls = class_create(THIS_MODULE, "myled");
if (IS_ERR(cls))
{
printk("向上提交目录失败\n");
ret = -PTR_ERR(cls);
goto out4;
}
printk("向上提交目录成功\n");
// 6.向上提交设备节点
for (i = 0; i < 3; i++)
{
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
if (IS_ERR(dev))
{
printk("向上提交节点信息失败\n");
ret = -PTR_ERR(dev);
goto out5;
}
}
printk("向上提交设备节点信息成功\n");
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
// 获取设备信息
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL)
{
printk("获取资源失败\n");
return -ENXIO;
}
printk("获取资源信息成功 %x\n", res->start);
irqno = platform_get_irq(pdev, 0);
if (irqno < 0)
{
printk("获取中断资源失败\n");
return irqno;
}
printk("中断类型资源为%d\n", irqno);
// 注册中断
ret = request_irq(irqno, myirq_handler, IRQF_TRIGGER_FALLING, "key", NULL);
if (ret)
{
printk("注册驱动失败\n");
return ret;
}
printk("key1中断注册成功\n");
// 获取gpio信息
// pdev->dev.of_node 设备树匹配之后会把设备树节点结构体首地址赋值给dev的of_node成员
gpiono = gpiod_get_from_of_node(pdev->dev.of_node, "led1", 0, GPIOD_OUT_LOW, NULL);
if (IS_ERR(gpiono))
{
printk("解析GPIO信息失败\n");
return -PTR_ERR(gpiono);
}
// 亮灯
gpiod_set_value(gpiono, 1);
return 0;
out5:
for (--i; i >= 0; i--)
{
// 销毁上面提交的设备信息
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
out4:
cdev_del(cdev);
out3:
unregister_chrdev_region(MKDEV(major, minor), 3);
out2:
kfree(cdev);
out1:
return ret;
}
//remove 设备和驱动分离执行
int pdrv_remove(struct platform_device *pdev)
{
printk("%s %s %d\n",__FILE__,__func__,__LINE__);
//销毁设备信息
int i;
for(i=0;i<3;i++)
{
device_destroy(cls,MKDEV(major,i));
}
//销毁目录
class_destroy(cls);
//销毁对象
cdev_del(cdev);
//释放设备号
unregister_chrdev_region(MKDEV(major,minor),3);
//释放对象空间
kfree(cdev);
free_irq(irqno,0);
//灭灯
gpiod_set_value(gpiono,0);
//释放gpio信息
gpiod_put(gpiono);
return 0;
}
//构建设备树匹配的表
struct of_device_id oftable[]={
{
.compatible="hqyj,myplatform",
},
{
.compatible="hqyj,myplatform1",
},
{},//防止数组越界
};
struct platform_driver pdrv=
{
.probe=pdrv_probe,
.remove=pdrv_remove,
.driver=
{
.name="aaaaa",
.of_match_table=oftable,
},
};
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");
test.c
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<sys/ioctl.h>
#include<fcntl.h>
int main(int argc, char const *argv[])
{
int fd=open("/dev/myled0",O_RDWR);
int number;
if(fd<0)
{
printf("打开失败\n");
exit(-1);
}
while(1)
{
read(fd,&number,sizeof(number));
printf("number=%d\n",number);
}
return 0;
}