一、
我们之前学的字符设备的驱动,把设备关于输入输出的设备利用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");