驱动——中断编程(申请中断)

一、
我们之前学的字符设备的驱动,把设备关于输入输出的设备利用gpio设置成输出模式。但是如果是其它设备就不用这样配置。和裸机驱动一样。
只是读取了设备的状态信息,并且利用文件操作结构体和应用程序交互数据。

我们还支持中断模式。对设备申请中断。申请中断之后cpu就会监视设备,看是否发生了中断的触发方式。如果发生中断就会跳转到中断处理函数。

中断编程流程。
前提:像之前一样搭建好框架。申请设备号,申请设备节点。
1、
在设备树中写入我们要操作的设备
在编程过程中,需要定义自己的节点–描述当前设备用的中断号
EG: key_int_node{
compatible = “test_key”;
interrupt-parent = <&gpx1>;
interrupts = <2 4>;
}; //不知道为什么怎么写,以后讲设备树的时候会讲。
2、我们首先要获取中断号。(可以自定义一个获取函数get_irqno)
1)从设备名字中获取设备节点。
truct device_node *np = of_find_node_by_path("/key_int_node")
填设备名称,与我们写入设备树的名称要保持一致
2)从设备节点中获取中断号
int irqno = irq_of_parse_and_map(np, 0);
3、中断申请
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char * name, void * dev)
参数1: 设备对应的中断号
参数2: 中断的处理函数
typedef irqreturn_t (*irq_handler_t)(int, void *);
参数3:触发方式
#define IRQF_TRIGGER_NONE 0x00000000 //内部控制器触发中断的时候的标志
#define IRQF_TRIGGER_RISING 0x00000001 //上升沿
#define IRQF_TRIGGER_FALLING 0x00000002 //下降沿
#define IRQF_TRIGGER_HIGH 0x00000004 // 高点平
#define IRQF_TRIGGER_LOW 0x00000008 //低电平触发
参数4:中断的描述,自定义,主要是给用户查看的
/proc/interrupts
参数5:传递给参数2中函数指针的值
返回值: 正确为0,错误非0
4、设置对应的中断处理函数(自定义),并填入参数二中
————————————代码——————————————

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/uaccess.h>

#define KEY_ENTER 28
#define GPX1_CON 0X11000C20
#define REMP_SIZE 8

//按键信息结构体
struct key_event{
	int code; // 表示按键的类型:  home, esc, Q,W,E,R,T, ENTER
	int value; // 表示按下还是抬起 1 / 0
};


//设备描述结构体
struct dev_descrp{
	unsigned int dev_major; 	 //设备号
	struct class *cls; 	//类指针
	struct device *dev;		//设备节点指针
	void *base_addr;
	unsigned int irqno;
	struct key_event event;   //存储按键信息
};

struct dev_descrp *key_dev;

//文件结构体函数
ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fops){

	printk("-------%s--------\n",__FUNCTION__);
	int ret;

	ret = copy_to_user(buf, &key_dev->event,  count);
	if(ret > 0)
	{
		printk("copy_to_user error\n");
		return -EFAULT;
	}
	return 0;
}
ssize_t key_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fops){

	printk("-------%s--------\n",__FUNCTION__);
	return 0;
}
int key_drv_open (struct inode *inode, struct file *filp){


	printk("-------%s--------\n",__FUNCTION__);
	return 0;
}
int key_drv_clos (struct inode *inode, struct file *filp){

	printk("-------%s--------\n",__FUNCTION__);
	return 0;
}



//设备操控文件结构体
const struct file_operations my_fops = {
	.read = key_drv_read,
	.write = key_drv_write,
	.open = key_drv_open,
	.release = key_drv_clos,
};

//中断处理函数
irqreturn_t irq_int_handle (int irqno, void *devid)
{

	printk("-------%s-----\n",__FUNCTION__);
	//读取数据寄存器数据
	int vaule;
	vaule = readl(key_dev->base_addr + 4) & (0X1 << 2);
	printk("-------readl----ok!!\n");

//判断状态给按键信息结构体赋值
	if(vaule){
		printk("key3 up\n");
		key_dev->event.code = KEY_ENTER;
		key_dev->event.value = 0;
	}else {
		printk("key3 down\n");
		key_dev->event.code = KEY_ENTER;
		key_dev->event.value = 1;
	}

	return IRQ_HANDLED;
}


//获取中断号
int get_irqno(void){
	
	printk("-------%s-----\n",__FUNCTION__);

	//从获取设备节点
	struct device_node *np;
	np = of_find_node_by_path("/key_int_node");
	//从设备节点获取中断号
	int IRQNO = irq_of_parse_and_map( np , 0);

	return IRQNO;
}


static int  key_drv_init(void)
{
//1、初始化结构体
	key_dev = kzalloc(sizeof(struct dev_descrp),GFP_KERNEL);
	if(key_dev == NULL){
			printk("kzalloc failed\n");
			return -EFAULT;
	}else{
			printk("kzalloc ok!\n");
	}
//2、申请设备号
		key_dev->dev_major = register_chrdev(0, "key_int", &my_fops);
		if(key_dev->dev_major < 0){
			printk(" register_chrdev failed!\n");
			return -EFAULT;
			goto err0;
			}else{
			printk("register_chrdev ok!\n");
			}
//3、申请设备节点
	//3.1、创建类
		key_dev->cls = class_create(THIS_MODULE, "KEY_CLS");
		if(key_dev->cls < 0){
			printk(" class_create failed!\n");
			return -EFAULT;
			goto err1;
		}else{
			printk("class_create ok!\n");
			}
	//3.2、申请设备节点
		key_dev->dev = device_create(key_dev->cls, NULL,MKDEV(key_dev->dev_major, 0), NULL, "key0");
		if(key_dev->dev ==  0){
			printk(" device_create failed!\n");
			return -EFAULT;
			goto err2;
			}else{
			printk("device_create!\n");
			}
//4、地址映射
		key_dev->base_addr = ioremap(GPX1_CON, REMP_SIZE);
		printk("----%d----\n",key_dev->base_addr);
//5、申请中断和初始化设备
		//获取中断号
		key_dev->irqno = get_irqno();
		printk("IRQnomber= %d\n",key_dev->irqno);

		int ret = request_irq(key_dev->irqno,irq_int_handle,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "KRY3_EINT", NULL);
		
		printk("-----request_irq----- ok!!");
		
		if(ret != 0){
			printk("request_irq error\n");
			return ret;
		}

		return 0;
err2:
	class_destroy(key_dev->cls);
	
err1:
	unregister_chrdev(key_dev->dev_major, "key_int");

err0:
	kfree(key_dev);


}


static void key_drv_exit(void){
	iounmap(key_dev->base_addr);
	free_irq(key_dev->irqno, NULL);
	device_destroy(key_dev->cls, MKDEV(key_dev->dev_major,0));
	class_destroy(key_dev->cls);
	unregister_chrdev(key_dev->dev_major, "key_drv");
	kfree(key_dev);

}

module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值