驱动文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
/*
myplatform{
compatible = "jyp,myplatform";
interrupt-parent = <&gpiof>;
interrupts = <9 0>,<7 0>,<8 0>;
led1 = <&gpioe 10 0>;
led2 = <&gpiof 10 0>;
led3 = <&gpioe 8 0>;
};
*/
#define CNAME "mycdev_led_key"
#define COUNT 3
struct resource *res;
struct gpio_desc *gpioled[3]; //存放gpio编号
unsigned int number;
struct cdev* cdev;
int minor = 0;
#if 0
unsigned int major = 0;//动态申请
#else
unsigned int major = 500; //静态指定设备号
#endif
char kbuf[128] = { 0 };
struct class* cls;
struct device* dev;
//定义一个指向设备节点的指针
struct device_node* node;
struct property* pr; //属性结构体指针
unsigned int irqno[3] = { 0 };//存放软中断号
char* name[] = { "key1", "key2", "key3" };
int curno; //记录打开的是哪一个设备节点
//定义等待队列头
wait_queue_head_t wq;
//判断标志位
int condition;
//给定时器分配对象
struct timer_list mytimer;
//分配工作队列对象
struct work_struct work;
//定时器处理函数
void timer_handler(struct timer_list *timer)
{
//定时器时间到了就启动工作队列
schedule_work(&work);
}
//底半部处理函数
void work_func(struct work_struct *work)
{
printk("-----timer and workqueue test-----\n");
mod_timer(&mytimer,jiffies+HZ);
}
//中断处理函数
irqreturn_t key_irq(int irq, void* dev)
{
switch ((int)dev) {
case 0: {
printk("key1 down led1 invert\n");
number=gpiod_get_value(gpioled[2]);
gpiod_set_value(gpioled[2],!number);
} break;
case 1: {
printk("key2 down led2 invert\n");
number=gpiod_get_value(gpioled[1]);
gpiod_set_value(gpioled[1],!number);
} break;
case 2: {
printk("key3 down led3 invert\n");
number=gpiod_get_value(gpioled[0]);
gpiod_set_value(gpioled[0],!number);
} break;
}
number=!number;
//printk("number=%d\n",number);
//防止唤醒后马上休眠
condition=1;
wake_up_interruptible(&wq);
return IRQ_HANDLED;
}
int mycdev_open(struct inode* inode, struct file* filp)
{
curno = MINOR(inode->i_rdev);
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file* filp,
char __user* ubuf, size_t size, loff_t* offs)
{
int ret;
//判断打开的方式
if(filp->f_flags & O_NONBLOCK)
{
//非阻塞
return -EINVAL;
}
else
{
//阻塞
ret = wait_event_interruptible(wq,condition);
if(ret==-ERESTARTSYS)
{
printk("接收阻塞休眠\n");
return ret;
}
}
//printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
if (size > sizeof(number))
size = sizeof(number);
ret = copy_to_user(ubuf, &number, size);
if (ret) {
printk("copy data to user error\n");
return -EINVAL;
}
//将conditon设置为0,为后面调用考虑
condition=0;
return size;
}
ssize_t mycdev_write(struct file* filp,
const char __user* ubuf, size_t size, loff_t* offs)
{
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 data from user error\n");
return -EINVAL;
}
return size;
}
int mycdev_close(struct inode* inode, struct file* filp)
{
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,
};
int gpio_irq_init(struct device_node *node)
{
int i,ret;
//获取并注册中断
//解析得到软中断号
for(i=0;i<ARRAY_SIZE(irqno);i++)
{
irqno[i]=irq_of_parse_and_map(node,i);
if(irqno[i]==0)
{
printk("get irq number error\n");
return -EAGAIN;
}
//注册中断
ret = request_irq(irqno[i],key_irq,IRQF_TRIGGER_FALLING,name[i],(void *)i);
if(ret)
{
printk("request irq error\n");
return ret;
}
}
//获取gpio编号并初始化
gpioled[0]=gpiod_get_from_of_node(node,"led1",0,GPIOD_OUT_HIGH,NULL);
if(IS_ERR(gpioled[0]))
{
printk("获取GPIO编号失败\n");
return PTR_ERR(gpioled[0]);
}
printk("获取extend-led1编号成功\n");
gpioled[1]=gpiod_get_from_of_node(node,"led2",0,GPIOD_OUT_HIGH,NULL);
if(IS_ERR(gpioled[1]))
{
printk("获取GPIO编号失败\n");
return PTR_ERR(gpioled[1]);
}
printk("获取extend-led2编号成功\n");
gpioled[2]=gpiod_get_from_of_node(node,"led3",0,GPIOD_OUT_HIGH,NULL);
if(IS_ERR(gpioled[2]))
{
printk("获取GPIO编号失败\n");
return PTR_ERR(gpioled[2]);
}
printk("获取extend-led3编号成功\n");
return 0;
}
void gpio_irq_exit(void)
{
int i;
//释放中断
for(i=0;i<ARRAY_SIZE(irqno);i++)
{
free_irq(irqno[i],(void *)i);
}
//释放设备号
for (i = 0; i < 3; i++) {
// gpio_set_value(gpioled[i], 0);
gpiod_put(gpioled[i]);
}
}
int pdrv_probe(struct platform_device *pdr)
{
int ret, i;
dev_t devno;
//获取gpio编号和中断号
ret=gpio_irq_init(pdr->dev.of_node);
if(ret!=0)
{
printk("初始化led和irq失败\n");
return ret;
}
//工作队列对象初始化
INIT_WORK(&work,work_func);
//分步注册
// 1.分配对象
cdev = cdev_alloc();
if (cdev == NULL) {
printk("alloc cdev memory error\n");
ret = -ENOMEM;
goto ERR1;
}
// 2.对象初始化
cdev_init(cdev, &fops);
// 3.设备号的申请
if (major == 0) {
//动态申请
ret = alloc_chrdev_region(&devno, minor, COUNT, CNAME);
if (ret) {
printk("dynamic:request device number error\n");
goto ERR2;
}
major = MAJOR(devno);
minor = MINOR(devno);
} else {
//静态指定
ret = register_chrdev_region(MKDEV(major, minor), COUNT, CNAME);
if (ret) {
printk("static:request device number error\n");
goto ERR2;
}
}
// 4.字符设备驱动的注册
ret = cdev_add(cdev, MKDEV(major, minor), COUNT);
if (ret) {
printk("register chardev error\n");
goto ERR3;
}
// 5.自动创建设备节点
cls = class_create(THIS_MODULE, CNAME);
if (IS_ERR(cls)) {
printk("class create error\n");
ret = PTR_ERR(cls);
goto ERR4;
}
for (i = minor; i < minor + COUNT; i++) {
dev = device_create(cls, NULL, MKDEV(major, i), NULL, "my_led%d", i);
if (IS_ERR(dev)) {
printk("device create error\n");
ret = PTR_ERR(dev);
goto ERR5;
}
}
//初始化等待队列头
init_waitqueue_head(&wq);
//初始化定时器对象
mytimer.expires=jiffies+HZ;//定时1s
timer_setup(&mytimer,timer_handler,0);
//注册定时器
add_timer(&mytimer);
return 0;
ERR5:
for (--i; i >= minor; 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;
}
int pdrv_remove(struct platform_device *pdr)
{
int i;
printk("%s:%d\n",__FILE__,__LINE__);
//取消工作队列的执行
cancel_work_sync(&work);
//删除定时器对象
del_timer(&mytimer);
//释放设备号与中断
gpio_irq_exit();
//注销设备节点
for (i = minor; i < minor + 3; i++) {
device_destroy(cls, MKDEV(major, i));
}
class_destroy(cls);
//字符设备驱动的注销
cdev_del(cdev);
//设备号释放
unregister_chrdev_region(MKDEV(major, minor), COUNT);
//释放cdev的内存空间
kfree(cdev);
return 0;
}
//定义compatible表
struct of_device_id oftable[]={
{.compatible="jyp,myplatform"},
{},
};
MODULE_DEVICE_TABLE(jyp,oftable);//安装设备时,自动安装驱动
//定义并初始化对象
struct platform_driver pdrv = {
.probe=pdrv_probe,
.remove=pdrv_remove,
.driver={
.name = "test",
.of_match_table=oftable,//设备树匹配
},
};
module_platform_driver(pdrv); //一键注册宏
MODULE_LICENSE("GPL");//许可证
MODULE_AUTHOR("jyp");//作者信息
测试文件
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
int main(int argc, char const *argv[])
{
int num;
int fd=open("/dev/my_led0",O_RDWR);
int which;
if(fd<0)
{
printf("打开设备文件失败\n");
exit(-1);
}
printf("设备文件打开成功\n");
while(1)
{
read(fd,&num,sizeof(num));
printf("number:%d\n",num);
}
close(fd);
return 0;
}