嵌入式驱动初级-中断

news2024/10/6 2:26:30

文章目录

  • 前言
  • 一、Linux 中断 API 函数
  • 二、Linux 中断实现
  • 三、中断上半部与下半部
  • 四、下半部机制之tasklet ---- 基于软中断
  • 五、按键中断下半部机制之tasklet
  • 六、下半部机制之workqueue ----- 基于内核线程
  • 七、按键中断下半部机制之workqueue


前言

记录嵌入式驱动学习笔记

一、Linux 中断 API 函数

设备树中增加按键节点
在这里插入图片描述

mykey2_node {
	compatible = "mykey2,key2";
	key2-gpio = <&gpx1 1 0>;
	interrupt-parent = <&gpx1>;
	interrupts = <1 3>;
};

驱动层

A.中断号:每个中断都有一个中断号,通过中断号即可区分不同的中断
B.从设备树获取中断号
unsigned int irq_of_parse_and_map(struct device_node *node, int index);
C.在 Linux 内核中要想使用某个中断是需要申请的,request_irq 函数用于申请中断
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
D.中断处理函数:使用 request_irq 函数申请中断的时候需要设置中断处理函数
irqreturn_t (*irq_handler_t) (int, void *)
E.释放中断:void free_irq(unsigned int irq, void *dev_id);

unsigned int irq_of_parse_and_map(struct device_node *node, int index)/*
	功能:获得设备树中的中断号并进行映射
	参数:node:设备节点
		 index:序号
	返回值:成功:中断号	失败:错误码
*/
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
/*
参数:
	irq:所申请的中断号
	handler:该中断号对应的中断处理函数
	flags:中断触发方式或处理方式 
		触发方式:IRQF_TRIGGER_NONE 		//无触发
		 	 	 IRQF_TRIGGER_RISING 	//上升沿触发
			 	 IRQF_TRIGGER_FALLING  //下降沿触发
				IRQF_TRIGGER_HIGH  	//高电平触发
				IRQF_TRIGGER_LOW 		//低电平触发
		处理方式:
			   IRQF_DISABLED		//用于快速中断,处理中屏蔽所有中断
				IRQF_SHARED		  //共享中断
		name:中断名 /proc/interrupts
		dev:传递给中断例程的参数,共享中断时用于区分那个设备,一般为对应设备的结构体地址,无共享中断时写NULL
返回值:成功:0 失败:错误码
*/
typedef irqreturn_t (*irq_handler_t)(int, void *);
/*
参数:
	int:中断号
	void*:对应的申请中断时的dev_id
返回值:
	typedef enum irqreturn irqreturn_t;	//中断返回值类型
	enum irqreturn {
		IRQ_NONE	= (0 << 0),
		IRQ_HANDLED	= (1 << 0),
		IRQ_WAKE_THREAD	= (1 << 1),
	};
	返回IRQ_HANDLED表示处理完了,返回IRQ_NONE在共享中断表示不处理
*/
void free_irq(unsigned int irq, void *dev_id)/*
功能:释放中断号
参数:
	irq:设备号
	dev_id:共享中断时用于区分那个设备一般强转成设备号,无共享中断时写NULL
*/

二、Linux 中断实现

驱动层按键中断代码实现

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

#include "fs4412_key.h"


int major = 11;
int minor = 0;
int fs4412key2_num  = 1;

struct fs4412key2_dev
{
	struct cdev mydev;
	//gpio引脚
	int gpio;
	//终端号
	int irqno;
	//接收按键产生的数据
	struct keyvalue data;
	//是否有新的数据产生标志位
	int newflag;
	//中断属于异常上下文所以不可以使用阻塞引起睡眠 应该用自旋锁 实现读阻塞
	spinlock_t lock;

	wait_queue_head_t rq;
};

struct fs4412key2_dev *pgmydev = NULL;

int fs4412key2_open(struct inode *pnode,struct file *pfile)
{
	pfile->private_data =(void *) (container_of(pnode->i_cdev,struct fs4412key2_dev,mydev));
	return 0;
}

int fs4412key2_close(struct inode *pnode,struct file *pfile)
{

	return 0;
}

ssize_t fs4412key2_read(struct file *pfile,char __user *puser,size_t count,loff_t *p_pos)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
	int size = 0;
	int ret = 0;
	//要读取数据的大小要大于按键结构体的大小
	if(count < sizeof(struct keyvalue))
	{
		printk("expect read size is invalid\n");
		return -1;
	}

	spin_lock(&pmydev->lock);
	//没数据可读时
	if(!pmydev->newflag)
	{
		if(pfile->f_flags & O_NONBLOCK)
		{//非阻塞
			//解锁
			spin_unlock(&pmydev->lock);
			printk("O_NONBLOCK No Data Read\n");
			return -1;
		}
		else
		{//阻塞
			//解锁
			spin_unlock(&pmydev->lock);
			//判断是否来数据 不等于就睡眠 等于一就接受数据
			ret = wait_event_interruptible(pmydev->rq,pmydev->newflag == 1);
			if(ret)
			{
				printk("Wake up by signal\n");
				return -ERESTARTSYS;
			}
			spin_lock(&pmydev->lock);
		}
	}
	//调整接收数据的大小
	if(count > sizeof(struct keyvalue))
	{
		size = sizeof(struct keyvalue);
	}
	else
	{
		size = count;
	}
	//将数据拷贝到用户空间
	ret = copy_to_user(puser,&pmydev->data,size);
	if(ret)
	{
		spin_unlock(&pmydev->lock);
		printk("copy_to_user failed\n");
		return -1;
	}
	//拷贝成功将数据表示为变为0
	pmydev->newflag = 0;

	spin_unlock(&pmydev->lock);

	return size;
}

unsigned int fs4412key2_poll(struct file *pfile,poll_table *ptb)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
	unsigned int mask = 0;

	poll_wait(pfile,&pmydev->rq,ptb);

	spin_lock(&pmydev->lock);
	//有新的数据产生就可以读
	if(pmydev->newflag)
	{
		mask |= POLLIN | POLLRDNORM;
	}
	spin_unlock(&pmydev->lock);

	return mask;
}

struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = fs4412key2_open,
	.release = fs4412key2_close,
	.read = fs4412key2_read,
	.poll = fs4412key2_poll,
};
//中断处理函数
irqreturn_t key2_irq_handle(int no,void *arg)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
	int status1 = 0;
	int status2 = 0;
	int status = 0;
	//读取两次按键gpio管脚 中间间隔1毫秒 按键消抖
	status1 = gpio_get_value(pmydev->gpio);
	mdelay(1);
	status2 = gpio_get_value(pmydev->gpio);
	//不相等中断空
	if(status1 != status2)
	{
		return IRQ_NONE;
	}
	//按键等于0
	status = status1;

	spin_lock(&pmydev->lock);
	if(status == pmydev->data.status)
	{
		spin_unlock(&pmydev->lock);
		return IRQ_NONE;
	}

	pmydev->data.code = KEY2;
	pmydev->data.status = status;
	//按键按下有新的数据产生
	pmydev->newflag = 1;

	spin_unlock(&pmydev->lock);
	//唤醒阻塞 把等待队列里的任务唤醒
	wake_up(&pmydev->rq);

	return IRQ_HANDLED;
}

int __init fs4412key2_init(void)
{
	int ret = 0;
	dev_t devno = MKDEV(major,minor);
	//设备树节点指针
	struct device_node *pnode = NULL;
	//找出key2的设备节点
	pnode = of_find_node_by_path("/mykey2_node");
	if(NULL == pnode)
	{
		printk("find node failed\n");
		return -1;
	}


	pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev),GFP_KERNEL);
	if(NULL == pgmydev)
	{
		printk("kmallc for struct fs4412key2_dev failed\n");
		return -1;
	}
	//从设备树获取gpio编号 可能gpio号包含多个但咱们只有一个所以是0
	pgmydev->gpio = of_get_named_gpio(pnode,"key2-gpio",0);
	//从设备树获取中断号
	pgmydev->irqno = irq_of_parse_and_map(pnode,0);

	/*申请设备号*/
	ret = register_chrdev_region(devno,fs4412key2_num,"fs4412key2");
	if(ret)
	{
		ret = alloc_chrdev_region(&devno,minor,fs4412key2_num,"fs4412key2");
		if(ret)
		{
			kfree(pgmydev);
			pgmydev = NULL;
			printk("get devno failed\n");
			return -1;
		}
		major = MAJOR(devno);//容易遗漏,注意
	}

	/*给struct cdev对象指定操作函数集*/	
	cdev_init(&pgmydev->mydev,&myops);

	/*将struct cdev对象添加到内核对应的数据结构里*/
	pgmydev->mydev.owner = THIS_MODULE;
	cdev_add(&pgmydev->mydev,devno,fs4412key2_num);

	//等待队列头做初始化
	init_waitqueue_head(&pgmydev->rq);
	//自旋锁初始化
	spin_lock_init(&pgmydev->lock);
	//中断申请
	ret = request_irq(pgmydev->irqno,key2_irq_handle,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"fs4412key2",pgmydev);
	//申请失败
	if(ret)
	{
		printk("request_irq failed\n");
		cdev_del(&pgmydev->mydev);
		//释放动态分配内存
		kfree(pgmydev);
		//指针指向空
		pgmydev = NULL;
		//
		unregister_chrdev_region(devno,fs4412key2_num);
		return -1;
	}
	return 0;
}

void __exit fs4412key2_exit(void)
{
	dev_t devno = MKDEV(major,minor);
	//释放中断
	free_irq(pgmydev->irqno,pgmydev);

	cdev_del(&pgmydev->mydev);

	unregister_chrdev_region(devno,fs4412key2_num);

	kfree(pgmydev);
	pgmydev = NULL;
}


MODULE_LICENSE("GPL");

module_init(fs4412key2_init);
module_exit(fs4412key2_exit);

按键中断应用层代码实现

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include <stdio.h>

#include "fs4412_key.h"

int main(int argc,char *argv[])
{
	int fd = -1;
	struct keyvalue keydata = {0};
	int ret = 0;

	if(argc < 2)
	{
		printf("The argument is too few\n");
		return 1;
	}

	fd = open(argv[1],O_RDONLY);
	if(fd < 0)
	{
		printf("open %s failed\n",argv[1]);
		return 3;
	}

	while((ret = read(fd,&keydata,sizeof(keydata))) == sizeof(keydata))
	{
		if(keydata.status == KEY_DOWN)
		{
			printf("Key2 is down!\n");
		}
		else
		{
			printf("Key2 is up!\n");
		}
	}

	close(fd);
	fd = -1;
	return 0;
}

三、中断上半部与下半部

Linux 内核将中断分为上半部和下半部的主要目的就是实现中断处理函数的快进快出,那些对时间敏感、执行速度快的操作可以放到中断处理函数中,也就是上半部。剩下的所有工作都可以放到下半部去执行,比如在上半部将数据拷贝到内存中,关于数据的具体处理就可以放到下半部去执行。

1.如果要处理的内容不希望被其他中断打断,那么可以放到上半部。
2.如果要处理的任务对时间敏感,可以放到上半部。
3.如果要处理的任务与硬件有关,可以放到上半部

上半部处理很简单,直接编写中断处理函数就行了,关键是下半部该怎么做呢?Linux 内核提供了多种下半部机制。

1.软中断
2.tasklet
3.工作队列
任务机制
​workqueue ----- 内核线程 能睡眠 运行时间无限制
异常机制 ------- 不能睡眠 下半部执行时间不宜太长( < 1s)
​软中断 ---- 接口不方便
tasklet ----- 无具体延后时间要求时
​定时器 -----有具体延后时间要求时

四、下半部机制之tasklet ---- 基于软中断

驱动层

A.定义 taselet
struct tasklet_struct testtasklet;
B.tasklet 处理函数
void testtasklet_func(unsigned long data)
C.中断处理函数中调度 tasklet
tasklet_schedule(&testtasklet);
D. 驱动入口函数中初始化 tasklet
tasklet_init(&testtasklet, testtasklet_func, data);

//初始化tasklet
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)
//调度tasklet
void tasklet_schedule(struct tasklet_struct *t)
//参数:t:tasklet的结构体

五、按键中断下半部机制之tasklet

驱动层代码实现

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

#include "fs4412_key.h"


int major = 11;
int minor = 0;
int fs4412key2_num  = 1;

struct fs4412key2_dev
{
	struct cdev mydev;

	int gpio;
	int irqno;

	struct keyvalue data;
	int newflag;
	spinlock_t lock;

	wait_queue_head_t rq;
	//定义tasklet_struct类型的对象
	struct tasklet_struct tsk;
};

struct fs4412key2_dev *pgmydev = NULL;

int fs4412key2_open(struct inode *pnode,struct file *pfile)
{
	pfile->private_data =(void *) (container_of(pnode->i_cdev,struct fs4412key2_dev,mydev));
	return 0;
}

int fs4412key2_close(struct inode *pnode,struct file *pfile)
{

	return 0;
}

ssize_t fs4412key2_read(struct file *pfile,char __user *puser,size_t count,loff_t *p_pos)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
	int size = 0;
	int ret = 0;

	if(count < sizeof(struct keyvalue))
	{
		printk("expect read size is invalid\n");
		return -1;
	}

	spin_lock(&pmydev->lock);
	if(!pmydev->newflag)
	{
		if(pfile->f_flags & O_NONBLOCK)
		{//非阻塞
			spin_unlock(&pmydev->lock);
			printk("O_NONBLOCK No Data Read\n");
			return -1;
		}
		else
		{//阻塞
			spin_unlock(&pmydev->lock);
			ret = wait_event_interruptible(pmydev->rq,pmydev->newflag == 1);
			if(ret)
			{
				printk("Wake up by signal\n");
				return -ERESTARTSYS;
			}
			spin_lock(&pmydev->lock);
		}
	}

	if(count > sizeof(struct keyvalue))
	{
		size = sizeof(struct keyvalue);
	}
	else
	{
		size = count;
	}

	ret = copy_to_user(puser,&pmydev->data,size);
	if(ret)
	{
		spin_unlock(&pmydev->lock);
		printk("copy_to_user failed\n");
		return -1;
	}

	pmydev->newflag = 0;

	spin_unlock(&pmydev->lock);

	return size;
}

unsigned int fs4412key2_poll(struct file *pfile,poll_table *ptb)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
	unsigned int mask = 0;

	poll_wait(pfile,&pmydev->rq,ptb);

	spin_lock(&pmydev->lock);
	if(pmydev->newflag)
	{
		mask |= POLLIN | POLLRDNORM;
	}
	spin_unlock(&pmydev->lock);

	return mask;
}

struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = fs4412key2_open,
	.release = fs4412key2_close,
	.read = fs4412key2_read,
	.poll = fs4412key2_poll,
};
//上半部结束时调度中断处理下半部
irqreturn_t key2_irq_handle(int no,void *arg)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;

	tasklet_schedule(&pmydev->tsk);

	return IRQ_HANDLED;
}
//中断下半部处理函数
void bottom_irq_func(unsigned long arg)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
	int status1 = 0;
	int status2 = 0;
	int status = 0;

	status1 = gpio_get_value(pmydev->gpio);
	mdelay(1);
	status2 = gpio_get_value(pmydev->gpio);

	if(status1 != status2)
	{
		return;
	}

	status = status1;

	spin_lock(&pmydev->lock);
	if(status == pmydev->data.status)
	{
		spin_unlock(&pmydev->lock);
		return;
	}

	pmydev->data.code = KEY2;
	pmydev->data.status = status;
	pmydev->newflag = 1;

	spin_unlock(&pmydev->lock);
	wake_up(&pmydev->rq);

	return;
}

int __init fs4412key2_init(void)
{
	int ret = 0;
	dev_t devno = MKDEV(major,minor);

	struct device_node *pnode = NULL;

	pnode = of_find_node_by_path("/mykey2_node");
	if(NULL == pnode)
	{
		printk("find node failed\n");
		return -1;
	}


	pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev),GFP_KERNEL);
	if(NULL == pgmydev)
	{
		printk("kmallc for struct fs4412key2_dev failed\n");
		return -1;
	}

	pgmydev->gpio = of_get_named_gpio(pnode,"key2-gpio",0);

	pgmydev->irqno = irq_of_parse_and_map(pnode,0);

	/*申请设备号*/
	ret = register_chrdev_region(devno,fs4412key2_num,"fs4412key2");
	if(ret)
	{
		ret = alloc_chrdev_region(&devno,minor,fs4412key2_num,"fs4412key2");
		if(ret)
		{
			kfree(pgmydev);
			pgmydev = NULL;
			printk("get devno failed\n");
			return -1;
		}
		major = MAJOR(devno);//容易遗漏,注意
	}

	/*给struct cdev对象指定操作函数集*/	
	cdev_init(&pgmydev->mydev,&myops);

	/*将struct cdev对象添加到内核对应的数据结构里*/
	pgmydev->mydev.owner = THIS_MODULE;
	cdev_add(&pgmydev->mydev,devno,fs4412key2_num);


	init_waitqueue_head(&pgmydev->rq);

	spin_lock_init(&pgmydev->lock);
	//初始化tasklet
	tasklet_init(&pgmydev->tsk,bottom_irq_func,(unsigned long)pgmydev);
	
	ret = request_irq(pgmydev->irqno,key2_irq_handle,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"fs4412key2",pgmydev);
	if(ret)
	{
		printk("request_irq failed\n");
		cdev_del(&pgmydev->mydev);
		kfree(pgmydev);
		pgmydev = NULL;
		unregister_chrdev_region(devno,fs4412key2_num);
		return -1;
	}
	return 0;
}

void __exit fs4412key2_exit(void)
{
	dev_t devno = MKDEV(major,minor);

	free_irq(pgmydev->irqno,pgmydev);

	cdev_del(&pgmydev->mydev);

	unregister_chrdev_region(devno,fs4412key2_num);

	kfree(pgmydev);
	pgmydev = NULL;
}


MODULE_LICENSE("GPL");

module_init(fs4412key2_init);
module_exit(fs4412key2_exit);

六、下半部机制之workqueue ----- 基于内核线程

工作队列是另外一种下半部执行方式,工作队列在进程上下文执行,工作队列将要推后的工作交给一个内核线程去执行,因为工作队列工作在进程上下文,因此工作队列允许睡眠或重新调度。因此如果你要推后的工作可以睡眠那么就可以选择工作队列,否则的话就只能选择软中断或 tasklet。
驱动层API

A.定义一个 work_struct 结构体变量
struct work_struct testwork;
B.驱动入口函数中初始化 work
INIT_WORK(&testwork, testwork_func_t);
C.定义work 处理函数
void testwork_func_t(struct work_struct *work);
D. 中断处理函数中调度work 处理函数
schedule_work(&testwork);

//定义work_struct 结构体变量
struct work_struct  work_queue;
//初始化工作队列
INIT_WORK(struct work_struct * pwork, _func);
//参数:pwork:工作队列
//func:工作队列的底半部处理函数
//定义工作队列底半部处理函数
void work_queue_func(struct work_struct  *work);
//工作队列的调度函数
bool schedule_work(struct work_struct *work)

七、按键中断下半部机制之workqueue

驱动层代码实现

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

#include "fs4412_key.h"


int major = 11;
int minor = 0;
int fs4412key2_num  = 1;

struct fs4412key2_dev
{
	struct cdev mydev;

	int gpio;
	int irqno;

	struct keyvalue data;
	int newflag;
	spinlock_t lock;

	wait_queue_head_t rq;

	//struct tasklet_struct tsk;工作队列对象
	struct work_struct wk;
};

struct fs4412key2_dev *pgmydev = NULL;

int fs4412key2_open(struct inode *pnode,struct file *pfile)
{
	pfile->private_data =(void *) (container_of(pnode->i_cdev,struct fs4412key2_dev,mydev));
	return 0;
}

int fs4412key2_close(struct inode *pnode,struct file *pfile)
{

	return 0;
}

ssize_t fs4412key2_read(struct file *pfile,char __user *puser,size_t count,loff_t *p_pos)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
	int size = 0;
	int ret = 0;

	if(count < sizeof(struct keyvalue))
	{
		printk("expect read size is invalid\n");
		return -1;
	}

	spin_lock(&pmydev->lock);
	if(!pmydev->newflag)
	{
		if(pfile->f_flags & O_NONBLOCK)
		{//非阻塞
			spin_unlock(&pmydev->lock);
			printk("O_NONBLOCK No Data Read\n");
			return -1;
		}
		else
		{//阻塞
			spin_unlock(&pmydev->lock);
			ret = wait_event_interruptible(pmydev->rq,pmydev->newflag == 1);
			if(ret)
			{
				printk("Wake up by signal\n");
				return -ERESTARTSYS;
			}
			spin_lock(&pmydev->lock);
		}
	}

	if(count > sizeof(struct keyvalue))
	{
		size = sizeof(struct keyvalue);
	}
	else
	{
		size = count;
	}

	ret = copy_to_user(puser,&pmydev->data,size);
	if(ret)
	{
		spin_unlock(&pmydev->lock);
		printk("copy_to_user failed\n");
		return -1;
	}

	pmydev->newflag = 0;

	spin_unlock(&pmydev->lock);

	return size;
}

unsigned int fs4412key2_poll(struct file *pfile,poll_table *ptb)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
	unsigned int mask = 0;

	poll_wait(pfile,&pmydev->rq,ptb);

	spin_lock(&pmydev->lock);
	if(pmydev->newflag)
	{
		mask |= POLLIN | POLLRDNORM;
	}
	spin_unlock(&pmydev->lock);

	return mask;
}

struct file_operations myops = {
	.owner = THIS_MODULE,
	.open = fs4412key2_open,
	.release = fs4412key2_close,
	.read = fs4412key2_read,
	.poll = fs4412key2_poll,
};
//上半部
irqreturn_t key2_irq_handle(int no,void *arg)
{
	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;

	//tasklet_schedule(&pmydev->tsk);
	//任务调度下半部
	schedule_work(&pmydev->wk);

	return IRQ_HANDLED;
}

//void bottom_irq_func(unsigned long arg)
//下半部
void bottom_irq_func(struct work_struct *pwk)
{
	//struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
	//已知结构体地址获取其成员地址
	struct fs4412key2_dev *pmydev = container_of(pwk,struct fs4412key2_dev,wk);
	int status1 = 0;
	int status2 = 0;
	int status = 0;

	status1 = gpio_get_value(pmydev->gpio);
	mdelay(1);
	status2 = gpio_get_value(pmydev->gpio);

	if(status1 != status2)
	{
		return;
	}

	status = status1;

	spin_lock(&pmydev->lock);
	if(status == pmydev->data.status)
	{
		spin_unlock(&pmydev->lock);
		return;
	}

	pmydev->data.code = KEY2;
	pmydev->data.status = status;
	pmydev->newflag = 1;

	spin_unlock(&pmydev->lock);
	wake_up(&pmydev->rq);

	return;
}

int __init fs4412key2_init(void)
{
	int ret = 0;
	dev_t devno = MKDEV(major,minor);

	struct device_node *pnode = NULL;

	pnode = of_find_node_by_path("/mykey2_node");
	if(NULL == pnode)
	{
		printk("find node failed\n");
		return -1;
	}


	pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev),GFP_KERNEL);
	if(NULL == pgmydev)
	{
		printk("kmallc for struct fs4412key2_dev failed\n");
		return -1;
	}

	pgmydev->gpio = of_get_named_gpio(pnode,"key2-gpio",0);

	pgmydev->irqno = irq_of_parse_and_map(pnode,0);

	/*申请设备号*/
	ret = register_chrdev_region(devno,fs4412key2_num,"fs4412key2");
	if(ret)
	{
		ret = alloc_chrdev_region(&devno,minor,fs4412key2_num,"fs4412key2");
		if(ret)
		{
			kfree(pgmydev);
			pgmydev = NULL;
			printk("get devno failed\n");
			return -1;
		}
		major = MAJOR(devno);//容易遗漏,注意
	}

	/*给struct cdev对象指定操作函数集*/	
	cdev_init(&pgmydev->mydev,&myops);

	/*将struct cdev对象添加到内核对应的数据结构里*/
	pgmydev->mydev.owner = THIS_MODULE;
	cdev_add(&pgmydev->mydev,devno,fs4412key2_num);


	init_waitqueue_head(&pgmydev->rq);

	spin_lock_init(&pgmydev->lock);

	//tasklet_init(&pgmydev->tsk,bottom_irq_func,(unsigned long)pgmydev);
	//初始化工作队列
	INIT_WORK(&pgmydev->wk,bottom_irq_func);
	
	ret = request_irq(pgmydev->irqno,key2_irq_handle,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"fs4412key2",pgmydev);
	if(ret)
	{
		printk("request_irq failed\n");
		cdev_del(&pgmydev->mydev);
		kfree(pgmydev);
		pgmydev = NULL;
		unregister_chrdev_region(devno,fs4412key2_num);
		return -1;
	}
	return 0;
}

void __exit fs4412key2_exit(void)
{
	dev_t devno = MKDEV(major,minor);

	free_irq(pgmydev->irqno,pgmydev);

	cdev_del(&pgmydev->mydev);

	unregister_chrdev_region(devno,fs4412key2_num);

	kfree(pgmydev);
	pgmydev = NULL;
}


MODULE_LICENSE("GPL");

module_init(fs4412key2_init);
module_exit(fs4412key2_exit);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/37496.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一文带你深入理解【Java基础】· 枚举类

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…

Vision Transformer这两年

作者&#xff5c;Maximilian Schambach OneFlow编译 翻译&#xff5c;胡燕君、杨婷 在NLP领域取得巨大成功后&#xff0c;Transformer架构在计算机视觉方面的作用日渐凸显&#xff0c;成为越来越普遍的CV工具。自2020年10月Vision Transformer模型推出以来&#xff0c;人们开始…

《研究生学术与职业素养讲座》第七讲~第九讲作业答案

第七讲 八千里路云和月—2015年意大利米兰世博会中国馆设计 填空题&#xff08;1分&#xff09;单选题&#xff08;1分&#xff09;判断题&#xff08;1分&#xff09;多选题&#xff08;2分&#xff09;第八讲 从纳米研究看工程创新 填空题&#xff08;1分&#xff09;单…

干货 | 一条语句更新多个表

众所周知&#xff0c;多个服务器命中会减慢应用程序的速度。出于这个原因&#xff0c;开发人员致力于找尋使用最少的语句更新数据的最有效方法。事实证明&#xff0c;SQL UPDATE 语句确实支持使用以下语法设置多个表的字段&#xff1a; UPDATE table1, table2, ...SET column1…

2022-11-14 西安 activiti工作流(01)

语言确实有其局限性&#xff0c;但我相信:一件值得做的事情即使做的不怎么样也是值得的! 概念 1.流程审批以前的实现方式 在没有专门的工作流引擎之前&#xff0c;为了实现流程控制&#xff0c;通常的做法就是采用状态字段的值来跟踪流程的变化情况。通过状态字段的取值来决定…

【数据结构】二叉树优先级队列——堆

文章目录1. 树的概念及结构1.1 树的相关概念1.2 树的表示2. 二叉树的概念及其结构2.1 二叉树的概念2.2 特殊的二叉树2.3 二叉树的性质2.4 二叉树的存储结构3. 堆3.1 堆的概念及结构3.2 堆的实现3.2.1 堆的创建3.2.2 堆的插入3.2.3 堆的向上调整算法3.2.4 堆的删除3.2.5 堆的向下…

javaSE--数据类型(复习)

一、变量和类型 变量 指的是 程序运行时 的 可变的量&#xff0c;相当于开辟一块内存空间来保存一些数据 类型 则是 对 变量的种类 进行了 划分&#xff0c;不同的类型 的 变量 具有不同特性 我们所讨论的"变量"主要 和 我们的"内存"这样的硬件设备密切相关…

value_counts()与count()的简单介绍

文章目录一&#xff0c;value_counts()&#xff08;一&#xff09;用法(二)参数介绍二&#xff0c;count()一&#xff0c;value_counts() &#xff08;一&#xff09;用法 value_counts&#xff08;&#xff09;是针对某一列的数据中存在不同的值进行汇总计算 举例 data[dis…

昨天阅读量900

昨天周五了&#xff0c;阅读量还不错的&#xff0c;超过平均值700的&#xff0c;一看有900多呢

80211 TIM流量指示图 附C语言实现

TIM是什么&#xff1f; TIM&#xff1a;Traffic Indication Map&#xff0c;流量指示图。 在80211协议节能模式中&#xff0c;AP会缓存下行数据&#xff0c;AP就是通过beacon帧中TIM来告知休眠中的STA有数据需要接收。 DTIM&#xff1a;Delivery Traffic Indication Map&#…

css引入其它字体

1、下载需要的字体&#xff0c;放到下图的文件夹中。 2、把下载的字体文件放到项目的font目录下 3、在css中加入下面的代码 font-face {font-family: "思源黑体";src: url("../font/SourceHanSansCN-Normal.ttf"); } * { font-family: "思源黑体&quo…

【计算机网络】Cookie、Session和上传文件重点知识汇总

目录 1.Cookie基础知识&#xff1a; 2.Session基础知识&#xff1a; 3.相关API&#xff1a; 3.1.HttpServletRequest类&#xff1a; 3.2.HttpServletResponse类&#xff1a; 3.3.HttpSession类&#xff1a; 3.4.Cookie类&#xff1a; 3.5.模拟实现登录页面 4.上传文件…

伸展树原理介绍

一 点睛 伸展树&#xff0c;也叫作分裂树&#xff0c;是一种二叉搜索树&#xff0c;可以在 O (logn ) 内完成插入、查找和删除操作。在任意数据结构的生命周期内执行不同操作的概率往往极不均衡&#xff0c;而且各操作之间有极强的相关性&#xff0c;在整体上多呈现极强的规律…

【LeetCode】882. 细分图中的可到达节点

题目描述 给你一个无向图&#xff08;原始图&#xff09;&#xff0c;图中有 n 个节点&#xff0c;编号从 0 到 n - 1 。你决定将图中的每条边 细分 为一条节点链&#xff0c;每条边之间的新节点数各不相同。 图用由边组成的二维数组 edges 表示&#xff0c;其中 edges[i] [ui…

ESG,TO B长期主义里的「新战役」

中国企业最好的方式是从初始阶段就植入ESG基因&#xff0c;使它逐渐从隐形变成显性基因。长期坚持此类发展导向&#xff0c;对后续发展ESG战略&#xff0c;提升ESG合规能力也将成为一种积累和准备。 作者|三七 编辑|皮爷 出品|产业家 20世纪初期&#xff0c;伦敦得到一个延…

MyBaits-Plus中@TableField和@TableId用法

目录 前言 一、TableField(value "表字段") 二、TableField(select false) 三、TableField(exist false) 四、TableId(type IdType.AUTO)补充 总结 前言 接着前两篇mybatis-plus的知识点 Mybatis-Plus知识点[MyBatisMyBatis-Plus的基础运用]_心态还需努力呀…

论文阅读: Disentangled lmage Colorization via Global Anchors

Disentangled lmage Colorization via Global Anchors发表于SIGGRAPH ASIA 2022&#xff0c;是一篇基于深度学习的图像彩色化的工作&#xff0c;简单介绍一下。之前曾分享过一篇彩色化的经典论文&#xff1a;经典论文回顾: Colorization using Optimization。 作者认为图像彩色…

【数据库】索引

MySQL索引 1、B树索引 是InnoDB引擎默认的索引 B树结构 B树是平衡树&#xff0c;即所有叶子节点都在同一层的多叉树 每个节点中key和指针交替排列&#xff0c;两个key之间的指针指向的是大于等于左边key且小于等于右边key的节点 叶子节点顺次连接&#xff0c;所以沿着B树的叶…

ADAU1860调试心得(5)ADC-DAC直通程序

硬件、驱动全部都搞好了&#xff0c;我觉得调试记录的话&#xff0c;就从最简单的开始&#xff0c;先做一个直通的例程。先把这个板子弄出声来&#xff0c;会用sigmastudio&#xff0c;会做ADAU开发的兄弟们应该很熟悉&#xff0c;同样的配方&#xff0c;同样的味道&#xff0c…

Nginx--单向链表分析

1.基本数据结构 1.1结点 struct ngx_list_part_s {void *elts;ngx_uint_t nelts;ngx_list_part_t *next; };结构成员分析 void* elts :数组元素指针 ngx_uint_t :数组里面的元素数量 ngx_list_part_t*…