Linux驱动_中断

裸机实验里面中断的处理方法:
        ①、使能中断,初始化相应的寄存器。
        ②、注册中断服务函数,也就是向 irqTable 数组的指定标号处写入中断服务函数
        ②、中断发生以后进入 IRQ 中断服务函数,在 IRQ 中断服务函数在数组 irqTable 里面查找
具体的中断处理函数,找到以后执行相应的中断处理函数。

        Linux 内核提供了完善的中断框架,我们只需要申请中断,然后注册中断处理函数即可,使用非常方便,不需要一系列复杂的寄存器配置。


一、中断API函数

        1、request_irq 函数

        在 Linux 内核中要想使用某个中断是需要申请的, request_irq 函数用于申请中断, request_irq函数可能会导致睡眠,因此不能在中断上下文或者其他禁止睡眠的代码段中使用 request_irq 函数。

int request_irq(unsigned int        irq,irq_handler_t        handler,unsigned long        flags,
                                const char        *name,void        *dev)

irq:要申请中断的中断号。
handler:中断处理函数,当中断发生以后就会执行此中断处理函数。
flags:中断标志。
name:中断名字,设置以后可以在/proc/interrupts 文件中看到对应的中断名字。
dev: 如果将 flags 设置为 IRQF_SHARED 的话, dev 用来区分不同的中断,一般情况下将
dev 设置为设备结构体, dev 会传递给中断处理函数 irq_handler_t 的第二个参数。
返回值: 0 中断申请成功,其他负值 中断申请失败,如果返回-EBUSY 的话表示中断已经
被申请了。

         常用的中断标志如下所示:  

         2、 free_irq 函数

        使用中断的时候需要通过 request_irq 函数申请,使用完成以后就要通过 free_irq 函数释放
掉相应的中断。如果中断不是共享的,那么 free_irq 会删除中断处理函数并且禁止中断。

void free_irq(unsigned int irq,void *dev)
irq: 要释放的中断。
dev:如果中断设置为共享(IRQF_SHARED)的话,此参数用来区分具体的中断。共享中断
只有在释放最后中断处理函数的时候才会被禁止掉。
返回值:无。 

          3、 中断处理函数

        中断处理函数格式如下所示:

irqreturn_t (*irq_handler_t) (int, void *)

第一个参数是要中断处理函数要相应的中断号。
第二个参数是一个指向 void 的指针,也就是个通用指针,需要与 request_irq 函数的 dev 参数保持一致。用于区分共享中断的不同设备,dev 也可以指向设备数据结构。

        中断处理函数的返回值为 irqreturn_t 类型, irqreturn_t 类型定义如下所示:

enum irqreturn {
 IRQ_NONE = (0 << 0),
 IRQ_HANDLED = (1 << 0),
 IRQ_WAKE_THREAD = (1 << 1),
};

typedef enum irqreturn irqreturn_t;

        一般中断函数返回值:return IRQ_RETVAL(IRQ_HANDLED)

        4、中断使能与禁止函数

void enable_irq(unsigned int irq)
void disable_irq(unsigned int irq)

        enable_irq 和 disable_irq 用于使能和禁止指定的中断, irq 就是要禁止的中断号。
        disable_irq函数要等到当前正在执行的中断处理函数执行完才返回,因此需要保证不会产生新的中断,并且确保所有已经开始执行的中断处理程序已经全部退出。

void disable_irq_nosync(unsigned int irq)

        disable_irq_nosync函数不会等待中断结束,会立即返回。

local_irq_enable()
local_irq_disable()        /*两个函数用于开启和关闭全局中断*/

二、上半部和下半部

      中断处理函数一定要快点执行完毕,越短越好!!因此引入中断上半部和下半部。

        上半部:上半部就是中断处理函数,那些处理过程比较快,不会占用很长时间的处理就可
以放在上半部完成。
        下半部:如果中断处理过程比较耗时,那么就将这些比较耗时的代码提出来,交给下半部
去执行,这样中断处理函数就会快进快出。

        1.tasklet

        tasklet 是利用软中断来实现的另外一种下半部机制,在软中断和 tasklet 之间,建议大家使
用 tasklet。 Linux 内核使用 tasklet_struct 结构体来表示 tasklet:

struct tasklet_struct{
    struct tasklet_struct *next; /* 下一个 tasklet */
    unsigned long state; /* tasklet 状态 */
    atomic_t count; /* 计数器,记录对 tasklet 的引用数 */
    void (*func)(unsigned long); /* tasklet 执行的函数 */
    unsigned long data; /* 函数 func 的参数 */
};

        tasklet使用框架:

/* 定义 taselet */
struct tasklet_struct testtasklet;
/* tasklet 处理函数 */
void testtasklet_func(unsigned long data)
{
/* tasklet 具体处理内容 */
}
/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{
......
/* 调度 tasklet */
tasklet_schedule(&testtasklet);
......
}
/* 驱动入口函数 */
static int __init xxxx_init(void)
{
......
/* 初始化 tasklet */
tasklet_init(&testtasklet, testtasklet_func, data);
/* 注册中断处理函数 */
request_irq(xxx_irq, test_handler, 0, "xxx", &xxx_dev);
......
}

        2、工作队列

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

        Linux 内核使用 work_struct 结构体表示一个工作:

struct work_struct {
    atomic_long_t data;
    struct list_head entry;
    work_func_t func; /* 工作队列处理函数 */
};

        工作队列的参考使用示例如下所示:

/* 定义工作(work) */
struct work_struct testwork;
/* work 处理函数 */
void testwork_func_t(struct work_struct *work);
{
/* work 具体处理内容 */
}
/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id)
{
......
/* 调度 work */
schedule_work(&testwork);
......
}
/* 驱动入口函数 */
static int __init xxxx_init(void)
{
......
/* 初始化 work */
INIT_WORK(&testwork, testwork_func_t);
/* 注册中断处理函数 */
request_irq(xxx_irq, test_handler, 0, "xxx", &xxx_dev);
......
}

 实验代码:

        一:通用

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <linux/interrupt.h>

#define KEY_NUM 1

#define IMX6ULIRQ_NAME "imx6ulirq"
#define IMX6ULIRQ_COUNT 1

#define KEY0VALUE 0X01
#define INVALKEY 0XFF

/*按键结构体*/
struct irq_keydesc{
    int gpio;  /*io编号*/
    int irqnum; /*中断编号*/
    unsigned char value; /*按键值*/
    char name[10];  /*中断名字*/
    irqreturn_t (*handler) (int, void *);  /*中断处理函数*/
};

/*设备结构体*/
struct imx6ulirq_dev{
    dev_t devid;    /*设备号*/
    int major;  /*主设备号*/
    int minor;  /*次设备号*/
    struct cdev cdev;   /*字符设备*/
    struct class *class;    /*创建类*/
    struct device *device;  /*创建设备*/
    struct device_node *node; /*设备节点*/
    struct irq_keydesc irqkey[KEY_NUM];     /*为什么定义一个按键结构体数组?-->因为一个产品,肯定不只一个按键,用此结构体保存所有按键*/
    struct timer_list timer;    /*添加定时器*/
    atomic_t keyval; /*按键值*/
    atomic_t releasekey;
};


/*中断处理函数*/
static irqreturn_t key0_handler(int irq, void *dev_id){
    struct imx6ulirq_dev *dev = dev_id;
 
    dev->timer.data = (volatile long)dev_id;
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(15)); /*15ms定时,起到消抖的作用*/
     
    return IRQ_HANDLED;
}

/*定时器超时处理函数*/
static void timer_func(unsigned long arg) {
    int value = 0;
    struct imx6ulirq_dev *dev = (struct imx6ulirq_dev *)arg;
 
    value = gpio_get_value(dev->irqkey[0].gpio);
    if(value == 0 ){    /*value==0表示按键被按下*/
        atomic_set(&dev->keyval, dev->irqkey[0].value); /*如果按键按下设置按键为设置的值*/
    }
    else if(value == 1){
        atomic_set(&dev->keyval, 0X80 | (dev->irqkey[0].value));/*松开则设置按键为设置的值高位或1,来区分*/
        atomic_set(&dev->releasekey, 1);    /*releasekey设置为1表示一次完整的按键过程*/
    }
}

struct imx6ulirq_dev imx6ulirq;    //定义一个设备

 
static int imx6ulirq_open(struct inode *inode, struct file *filp){
    filp->private_data = &imx6ulirq; /* 设置私有数据 */
    return 0;
}
 
 
static int imx6ulirq_release(struct inode *inode, struct file *filp){
    
    return 0;
}
 
static ssize_t imx6ulirq_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt){
    int ret = 0;
    unsigned char keyval;
    unsigned char releasekey;
    struct imx6ulirq_dev *dev = filp->private_data;
 
    keyval = atomic_read(&dev->keyval);
    releasekey = atomic_read(&dev->releasekey);
 
    if(releasekey){ /*如果releasekey为真表示一次完整的按键过程*/
        if(keyval & 0X80){  /*前面将真实按键值或了0X80,现在判断是否为真,若为真则返回原按键值*/
            keyval &= ~0X80;
            ret = copy_to_user(buf, &keyval, sizeof(keyval));
        }
        else{
            goto failed_error;
        }
        atomic_set(&dev->releasekey, 0);    /*按下标志清0*/
    }
    else{
        goto failed_error;
    }
 
    return 0;
failed_error:
    return -EINVAL;
 
}

static const struct file_operations imx6ulirq_fops = { /*字符设备操作函数集合*/
    .owner = THIS_MODULE,
    .open = imx6ulirq_open,
    .read = imx6ulirq_read,
    .release = imx6ulirq_release,
};



/*按键初始化*/
static int keyio_init(struct imx6ulirq_dev *dev){
    int ret = 0;
    int i = 0;
    /*1、按键初始化*/
    dev->node = of_find_node_by_path("/key");
    if(dev->node == NULL){
        ret = -EINVAL;
        goto failed_findnode;
    }
 
    /*循环找到节点*/
    for (i = 0; i < KEY_NUM; i++){
        dev->irqkey[i].gpio = of_get_named_gpio(dev->node, "key-gpios", i); 
        if(dev->irqkey[i].gpio < 0){
            printk("can't get gpio %d\r\n",i);
            ret = -EINVAL;
            goto failed_findnode;
        }   
    }
    /*循环申请gpio*/
     for (i = 0; i < KEY_NUM; i++){ 
        memset(dev->irqkey[i].name, 0, sizeof(dev->irqkey[i].name));
        sprintf(dev->irqkey[i].name, "KEY%d", i);
        ret = gpio_request(dev->irqkey[i].gpio, dev->irqkey[i].name);
        if(ret){
            printk("Failed to request gpio \r\n");
            ret = -EINVAL;
            goto failed_findnode;
    }
    ret = gpio_direction_input(dev->irqkey[i].gpio);
    if(ret < 0){
        goto failed_setinput;
     }
    dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio);   /*获取中断号*/
     }
    /*2、按键中断初始化*/
 
    dev->irqkey[0].handler = key0_handler;
    dev->irqkey[0].value = KEY0VALUE;
 
    for(i = 0;i< KEY_NUM; i++){
        ret = request_irq(dev->irqkey[i].irqnum, dev->irqkey[i].handler, 
                         IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, dev->irqkey[i].name, &imx6ulirq);
        if(ret){
            printk("irq %d request failed!\r\n",i);
            goto failed_irq;
        }
    }
 
    /*初始化定时器*/
    init_timer(&dev->timer);
    dev->timer.function = timer_func;    /*定时器超时处理函数*/
 
    return 0;
 
failed_irq:
failed_setinput:
    for(i = 0;i< KEY_NUM;i++) {
        gpio_free(dev->irqkey[i].gpio);
    }
failed_findnode:
    return ret;
}


static int __init imx6ulirq_init(void)
{
    int ret = 0;

    printk("imx6ulirq init\r\n");
    imx6ulirq.major = 0;    //申请自动分配设备号
    if(imx6ulirq.major){    //如果定义了主设备号,采用手动分配设备号的方式
        imx6ulirq.devid = MKDEV(imx6ulirq.major, 0);
        ret = register_chrdev_region(imx6ulirq.devid, IMX6ULIRQ_COUNT, IMX6ULIRQ_NAME);
    }
    else{   //否则自动分配设备号
        ret = alloc_chrdev_region(&imx6ulirq.devid, 0, IMX6ULIRQ_COUNT, IMX6ULIRQ_NAME);
        imx6ulirq.major = MAJOR(imx6ulirq.major);   //保存主设备号
        imx6ulirq.minor = MINOR(imx6ulirq.minor);   //保存次设备号
    }

    if(ret < 0){
        goto failed_devid;
    }
    /*打印主次设备号*/
    printk("imx6ulirq_dev major = %d, minor = %d\r\n",imx6ulirq.major, imx6ulirq.minor);

    /*向内核添加字符设备*/
    imx6ulirq.cdev.owner = THIS_MODULE;
    cdev_init(&imx6ulirq.cdev, &imx6ulirq_fops);    /*初始化字符设备*/
    ret = cdev_add(&imx6ulirq.cdev, imx6ulirq.devid, IMX6ULIRQ_COUNT);  /*向内核添加字符设备*/
    if(ret < 0){
        goto failed_cdev;
    }

    /*自动创建设备节点*/
    /*创建类*/
    imx6ulirq.class = class_create(THIS_MODULE, IMX6ULIRQ_NAME);
    if(IS_ERR(imx6ulirq.class)){    /*判断类是否创建成功*/
        ret = PTR_ERR(imx6ulirq.class);
        goto failed_class;
    }
    /*创建设备*/
    imx6ulirq.device = device_create(imx6ulirq.class, NULL, imx6ulirq.devid, NULL, IMX6ULIRQ_NAME);
    if(IS_ERR(imx6ulirq.device)){   /*判断是否创建类成功*/
        ret = PTR_ERR(imx6ulirq.device);
        goto failed_device;
    }


    /*初始化按键*/
    ret = keyio_init(&imx6ulirq);
    if(ret < 0){
        goto failed_keyio;
    }

     /*初始化原子变量*/
    atomic_set(&imx6ulirq.keyval, INVALKEY);
    atomic_set(&imx6ulirq.releasekey, 0);

    return 0;

failed_keyio:
failed_device:
    class_destroy(imx6ulirq.class);
failed_class:
    cdev_del(&imx6ulirq.cdev);
failed_cdev:
    unregister_chrdev_region(imx6ulirq.devid, IMX6ULIRQ_COUNT);
failed_devid:
    return ret;
}


/*出口函数*/
static void __exit imx6ulirq_exit(void){
    int i = 0;
 
    /*释放中断*/
    for(i = 0;i< KEY_NUM;i++) {
        free_irq(imx6ulirq.irqkey[i].irqnum, &imx6ulirq); 
    }
    /*释放io*/
     for(i = 0;i< KEY_NUM;i++) {
        gpio_free(imx6ulirq.irqkey[i].gpio);
    }
    
    /*删除定时器*/
    del_timer_sync(&imx6ulirq.timer);
 
    /*注销字符设备*/
    cdev_del(&imx6ulirq.cdev);
    /*卸载设备*/
    unregister_chrdev_region(imx6ulirq.devid, IMX6ULIRQ_COUNT);
 
    device_destroy(imx6ulirq.class, imx6ulirq.devid);
    class_destroy(imx6ulirq.class);
 
}

module_init(imx6ulirq_init);
module_exit(imx6ulirq_exit);

MODULE_AUTHOR("ZYC");
MODULE_LICENSE("GPL");  //协议

        二:tasklet

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
 
 
 
#define IMX6ULIRQ_COUNT 1
#define IMX6ULIRQ_NAME "tasklet"
 
#define KEY_NUM 1   /*因为一块板子上按键不只有1个,所有创建一个结构体数组来描述按键*/
#define KEY0VALUE 0X01
#define INVALKEY 0XFF
 
 
/*按键结构体*/
struct irq_keydesc{
    int gpio;  /*io编号*/
    int irqnum; /*中断编号*/
    unsigned char value; /*按键值*/
    char name[10];  /*中断名字*/
    irqreturn_t (*handler) (int, void *);  /*中断处理函数*/
    struct tasklet_struct tasklet;
};
 
/*设备结构体*/
struct imx6ulirq_dev{
    dev_t devid;    /*设备号*/
    int major;  /*主设备号*/
    int minor;  /*次设备号*/
    struct cdev cdev;   /*字符设备*/
    struct class *class;    /*创建类*/
    struct device *device;  /*创建设备*/
    struct device_node *node; /*设备节点*/
    struct irq_keydesc irqkey[KEY_NUM]; 
    struct timer_list timer;    /*添加定时器*/
    atomic_t keyval; /*按键值*/
    atomic_t releasekey;
 
};
 
 
struct imx6ulirq_dev imx6ulirq;
 
static int imx6ulirq_open(struct inode *inode, struct file *filp){
    filp->private_data = &imx6ulirq; /* 设置私有数据 */
    return 0;
}
 
 
static int imx6ulirq_release(struct inode *inode, struct file *filp){
    
    return 0;
}
 
static ssize_t imx6ulirq_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt){
    int ret = 0;
    unsigned char keyval;
    unsigned char releasekey;
    struct imx6ulirq_dev *dev = filp->private_data;
 
    keyval = atomic_read(&dev->keyval);
    releasekey = atomic_read(&dev->releasekey);
 
    if(releasekey){ /*如果releasekey为真表示一次完整的按键过程*/
        if(keyval & 0X80){  /*前面将真实按键值或了0X80,现在判断是否为真,若为真则返回原按键值*/
            keyval &= ~0X80;
            ret = copy_to_user(buf, &keyval, sizeof(keyval));
        }
        else{
            goto failed_error;
        }
        atomic_set(&dev->releasekey, 0);    /*按下标志清0*/
    }
    else{
        goto failed_error;
    }
 
    return 0;
failed_error:
    return -EINVAL;
 
}
 
 
 
static const struct file_operations imx6ulirq_fops = { /*字符设备操作函数集合*/
    .owner = THIS_MODULE,
    .open = imx6ulirq_open,
    .read = imx6ulirq_read,
    .release = imx6ulirq_release,
    
};
 
/*中断处理函数*/
static irqreturn_t key0_handler(int irq, void *dev_id){
    struct imx6ulirq_dev *dev = dev_id;
 
    tasklet_schedule(&dev->irqkey[0].tasklet);  /*在中断处理函数中调度tasklet*/
    return IRQ_HANDLED;
}
 
/*定时器超市处理函数*/
static void timer_func(unsigned long arg) {
    int value = 0;
    struct imx6ulirq_dev *dev = (struct imx6ulirq_dev *)arg;
 
    value = gpio_get_value(dev->irqkey[0].gpio);
    if(value == 0 ){
        atomic_set(&dev->keyval, dev->irqkey[0].value); /*如果按键按下设置按键为设置的值*/
    }
    else if(value == 1){
        atomic_set(&dev->keyval, 0X80 | (dev->irqkey[0].value));/*松开则设置按键为设置的值高位或1,来区分*/
        atomic_set(&dev->releasekey, 1);    /*releasekey设置为1表示一次完整的按键过程*/
    }
}
 
/*tasklet函数*/
static void key_tasklet(unsigned long data){
    struct imx6ulirq_dev *dev = (struct imx6ulirq_dev *)data;
 
 
  /*定时器延时*/
    dev->timer.data = data;
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(15)); /*15ms定时*/
 
}
 
/*按键初始化*/
static int keyio_init(struct imx6ulirq_dev *dev){
    int ret = 0;
    int i = 0;
    /*1、按键初始化*/
    dev->node = of_find_node_by_path("/key");
    if(dev->node == NULL){
        ret = -EINVAL;
        goto failed_findnode;
    }
 
    /*循环找到节点*/
    for (i = 0; i < KEY_NUM; i++){
        dev->irqkey[i].gpio = of_get_named_gpio(dev->node, "key-gpios", i); 
        if(dev->irqkey[i].gpio < 0){
            printk("can't get gpio %d\r\n",i);
            ret = -EINVAL;
            goto failed_findnode;
        }   
    }
    /*循环申请gpio*/
     for (i = 0; i < KEY_NUM; i++){ 
        memset(dev->irqkey[i].name, 0, sizeof(dev->irqkey[i].name));
        sprintf(dev->irqkey[i].name, "KEY%d", i);
        ret = gpio_request(dev->irqkey[i].gpio, dev->irqkey[i].name);
        if(ret){
            printk("Failed to request gpio \r\n");
            ret = -EINVAL;
            goto failed_findnode;
    }
    ret = gpio_direction_input(dev->irqkey[i].gpio);
    if(ret < 0){
        goto failed_setinput;
     }
    dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio);   /*获取中断号*/
     }
    /*2、按键中断初始化*/
 
    dev->irqkey[0].handler = key0_handler;
    dev->irqkey[0].value = KEY0VALUE;
    /*dev->irqkey[0].tasklet = key_tasklet;*/
 
    for(i = 0;i< KEY_NUM; i++){
        ret = request_irq(dev->irqkey[i].irqnum, dev->irqkey[i].handler, 
                         IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, dev->irqkey[i].name, &imx6ulirq);
        if(ret){
            printk("irq %d request failed!\r\n",i);
            goto failed_irq;
        }
    tasklet_init(&dev->irqkey[i].tasklet, key_tasklet, (unsigned long)dev); /*本处写死所有的按键都用的同一个tasklet,*/
 
    }
 
    /*初始化定时器*/
    init_timer(&dev->timer);
    dev->timer.function = timer_func;    /*定时器超时处理函数*/
 
    return 0;
 
failed_irq:
failed_setinput:
    for(i = 0;i< KEY_NUM;i++) {
        gpio_free(dev->irqkey[i].gpio);
    }
failed_findnode:
    return ret;
}
 
 
/*入口函数*/
static int __init imx6ulirq_init(void){
    int ret = 0;
 
 
    /*注册字符设备*/
    imx6ulirq.major = 0;     /*内核自动申请设备号*/
    if(imx6ulirq.major){     /*如果定义了设备号*/
        imx6ulirq.devid = MKDEV(imx6ulirq.major, 0);
        ret = register_chrdev_region(imx6ulirq.devid, IMX6ULIRQ_COUNT, IMX6ULIRQ_NAME); 
    }
    else{   /*否则自动申请设备号*/
        ret = alloc_chrdev_region(&imx6ulirq.devid, 0, IMX6ULIRQ_COUNT, IMX6ULIRQ_NAME);
        imx6ulirq.major = MAJOR(imx6ulirq.devid); /*保存主设备号*/
        imx6ulirq.minor = MINOR(imx6ulirq.devid); /*保存次设备号*/
    }
    if(ret < 0){
        goto failed_devid;
    }
    printk("imx6ulirq_dev major = %d minor = %d \r\n",imx6ulirq.major,imx6ulirq.minor);  /*打印主次设备号*/
 
    /*添加字符设备*/
    imx6ulirq.cdev.owner = THIS_MODULE;
    cdev_init(&imx6ulirq.cdev, &imx6ulirq_fops);
    ret = cdev_add(&imx6ulirq.cdev, imx6ulirq.devid, IMX6ULIRQ_COUNT);
    if(ret < 0){    /*添加字符设备失败*/
        goto failed_cdev;
    }
 
    /*自动添加设备节点*/
    /*创建类*/
    imx6ulirq.class = class_create(THIS_MODULE, IMX6ULIRQ_NAME); /*class_creat(owner,name);*/
    if(IS_ERR(imx6ulirq.class)){   /*判断是否创建类成功*/
        ret = PTR_ERR(imx6ulirq.class);
        goto failed_class;
    }
 
    /*创建设备*/
 
    imx6ulirq.device = device_create(imx6ulirq.class, NULL, imx6ulirq.devid, NULL, IMX6ULIRQ_NAME);
     if(IS_ERR(imx6ulirq.device)){   /*判断是否创建类成功*/
        ret = PTR_ERR(imx6ulirq.device);
        goto failed_device;
    }
 
    /*初始化IO*/
    ret = keyio_init(&imx6ulirq);
    if(ret < 0) {
        goto failed_keyinit;
    }
 
    /*初始化原子变量*/
    atomic_set(&imx6ulirq.keyval, INVALKEY);
    atomic_set(&imx6ulirq.releasekey, 0);
 
 
    return 0;
 
failed_keyinit:
failed_device:
    class_destroy(imx6ulirq.class);
failed_class:
    cdev_del(&imx6ulirq.cdev);
failed_cdev:
    unregister_chrdev_region(imx6ulirq.devid, IMX6ULIRQ_COUNT);
failed_devid:
    return ret;
}
 
/*出口函数*/
static void __exit imx6ulirq_exit(void){
    int i = 0;
 
    /*释放中断*/
    for(i = 0;i< KEY_NUM;i++) {
        free_irq(imx6ulirq.irqkey[i].irqnum, &imx6ulirq); 
    }
    /*释放io*/
     for(i = 0;i< KEY_NUM;i++) {
        gpio_free(imx6ulirq.irqkey[i].gpio);
    }
    
    /*删除定时器*/
    del_timer_sync(&imx6ulirq.timer);
 
    /*注销字符设备*/
    cdev_del(&imx6ulirq.cdev);
    /*卸载设备*/
    unregister_chrdev_region(imx6ulirq.devid, IMX6ULIRQ_COUNT);
 
    device_destroy(imx6ulirq.class, imx6ulirq.devid);
    class_destroy(imx6ulirq.class);
 
}
 
 
/*模块入口和出口*/
module_init(imx6ulirq_init);
module_exit(imx6ulirq_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYC");

        三:work

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
 
 
 
#define IMX6ULIRQ_COUNT 1
#define IMX6ULIRQ_NAME "work"
 
#define KEY_NUM 1   /*因为一块板子上按键不只有1个,所有创建一个结构体数组来描述按键*/
#define KEY0VALUE 0X01
#define INVALKEY 0XFF
 
 
/*按键结构体*/
struct irq_keydesc{
    int gpio;  /*io编号*/
    int irqnum; /*中断编号*/
    unsigned char value; /*按键值*/
    char name[10];  /*中断名字*/
    irqreturn_t (*handler) (int, void *);  /*中断处理函数*/
    struct tasklet_struct tasklet;   
    struct work_struct work;    /*工作队列*/
};
 
/*设备结构体*/
struct imx6ulirq_dev{
    dev_t devid;    /*设备号*/
    int major;  /*主设备号*/
    int minor;  /*次设备号*/
    struct cdev cdev;   /*字符设备*/
    struct class *class;    /*创建类*/
    struct device *device;  /*创建设备*/
    struct device_node *node; /*设备节点*/
    struct irq_keydesc irqkey[KEY_NUM]; 
    struct timer_list timer;    /*添加定时器*/
    atomic_t keyval; /*按键值*/
    atomic_t releasekey;
 
};
 
 
struct imx6ulirq_dev imx6ulirq;
 
static int imx6ulirq_open(struct inode *inode, struct file *filp){
    filp->private_data = &imx6ulirq; /* 设置私有数据 */
    return 0;
}
 
 
static int imx6ulirq_release(struct inode *inode, struct file *filp){
    
    return 0;
}
 
static ssize_t imx6ulirq_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt){
    int ret = 0;
    unsigned char keyval;
    unsigned char releasekey;
    struct imx6ulirq_dev *dev = filp->private_data;
 
    keyval = atomic_read(&dev->keyval);
    releasekey = atomic_read(&dev->releasekey);
 
    if(releasekey){ /*如果releasekey为真表示一次完整的按键过程*/
        if(keyval & 0X80){  /*前面将真实按键值或了0X80,现在判断是否为真,若为真则返回原按键值*/
            keyval &= ~0X80;
            ret = copy_to_user(buf, &keyval, sizeof(keyval));
        }
        else{
            goto failed_error;
        }
        atomic_set(&dev->releasekey, 0);    /*按下标志清0*/
    }
    else{
        goto failed_error;
    }
 
    return 0;
failed_error:
    return -EINVAL;
 
}
 
 
 
static const struct file_operations imx6ulirq_fops = { /*字符设备操作函数集合*/
    .owner = THIS_MODULE,
    .open = imx6ulirq_open,
    .read = imx6ulirq_read,
    .release = imx6ulirq_release,
    
};
 
/*中断处理函数*/
static irqreturn_t key0_handler(int irq, void *dev_id){
    struct imx6ulirq_dev *dev = dev_id;
 
    schedule_work(&dev->irqkey[0].work);  /*在中断处理函数中调度work*/
    return IRQ_HANDLED;
}
 
/*定时器超市处理函数*/
static void timer_func(unsigned long arg) {
    int value = 0;
    struct imx6ulirq_dev *dev = (struct imx6ulirq_dev *)arg;
 
    value = gpio_get_value(dev->irqkey[0].gpio);
    if(value == 0 ){
        atomic_set(&dev->keyval, dev->irqkey[0].value); /*如果按键按下设置按键为设置的值*/
    }
    else if(value == 1){
        atomic_set(&dev->keyval, 0X80 | (dev->irqkey[0].value));/*松开则设置按键为设置的值高位或1,来区分*/
        atomic_set(&dev->releasekey, 1);    /*releasekey设置为1表示一次完整的按键过程*/
    }
}

/*work函数*/
static void key_work(struct work_struct *work)
{
  /*定时器延时*/
    imx6ulirq.timer.data = (unsigned long)&imx6ulirq;
    mod_timer(&imx6ulirq.timer, jiffies + msecs_to_jiffies(15)); /*15ms定时*/
}

 
/*按键初始化*/
static int keyio_init(struct imx6ulirq_dev *dev){
    int ret = 0;
    int i = 0;
    /*1、按键初始化*/
    dev->node = of_find_node_by_path("/key");
    if(dev->node == NULL){
        ret = -EINVAL;
        goto failed_findnode;
    }
 
    /*循环找到节点*/
    for (i = 0; i < KEY_NUM; i++){
        dev->irqkey[i].gpio = of_get_named_gpio(dev->node, "key-gpios", i); 
        if(dev->irqkey[i].gpio < 0){
            printk("can't get gpio %d\r\n",i);
            ret = -EINVAL;
            goto failed_findnode;
        }   
    }
    /*循环申请gpio*/
     for (i = 0; i < KEY_NUM; i++){ 
        memset(dev->irqkey[i].name, 0, sizeof(dev->irqkey[i].name));
        sprintf(dev->irqkey[i].name, "KEY%d", i);
        ret = gpio_request(dev->irqkey[i].gpio, dev->irqkey[i].name);
        if(ret){
            printk("Failed to request gpio \r\n");
            ret = -EINVAL;
            goto failed_findnode;
    }
    ret = gpio_direction_input(dev->irqkey[i].gpio);
    if(ret < 0){
        goto failed_setinput;
     }
    dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio);   /*获取中断号*/
     }
    /*2、按键中断初始化*/
 
    dev->irqkey[0].handler = key0_handler;
    dev->irqkey[0].value = KEY0VALUE;
 
    for(i = 0;i< KEY_NUM; i++){
        ret = request_irq(dev->irqkey[i].irqnum, dev->irqkey[i].handler, 
                         IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, dev->irqkey[i].name, &imx6ulirq);
        if(ret){
            printk("irq %d request failed!\r\n",i);
            goto failed_irq;
        }
    INIT_WORK(&dev->irqkey[i].work, key_work);  /*所有按键采用同一个work函数*/
    }
 
    /*初始化定时器*/
    init_timer(&dev->timer);
    dev->timer.function = timer_func;    /*定时器超时处理函数*/
 
    return 0;
 
failed_irq:
failed_setinput:
    for(i = 0;i< KEY_NUM;i++) {
        gpio_free(dev->irqkey[i].gpio);
    }
failed_findnode:
    return ret;
}
 
 
/*入口函数*/
static int __init imx6ulirq_init(void){
    int ret = 0;
 
 
    /*注册字符设备*/
    imx6ulirq.major = 0;     /*内核自动申请设备号*/
    if(imx6ulirq.major){     /*如果定义了设备号*/
        imx6ulirq.devid = MKDEV(imx6ulirq.major, 0);
        ret = register_chrdev_region(imx6ulirq.devid, IMX6ULIRQ_COUNT, IMX6ULIRQ_NAME); 
    }
    else{   /*否则自动申请设备号*/
        ret = alloc_chrdev_region(&imx6ulirq.devid, 0, IMX6ULIRQ_COUNT, IMX6ULIRQ_NAME);
        imx6ulirq.major = MAJOR(imx6ulirq.devid); /*保存主设备号*/
        imx6ulirq.minor = MINOR(imx6ulirq.devid); /*保存次设备号*/
    }
    if(ret < 0){
        goto failed_devid;
    }
    printk("imx6ulirq_dev major = %d minor = %d \r\n",imx6ulirq.major,imx6ulirq.minor);  /*打印主次设备号*/
 
    /*添加字符设备*/
    imx6ulirq.cdev.owner = THIS_MODULE;
    cdev_init(&imx6ulirq.cdev, &imx6ulirq_fops);
    ret = cdev_add(&imx6ulirq.cdev, imx6ulirq.devid, IMX6ULIRQ_COUNT);
    if(ret < 0){    /*添加字符设备失败*/
        goto failed_cdev;
    }
 
    /*自动添加设备节点*/
    /*创建类*/
    imx6ulirq.class = class_create(THIS_MODULE, IMX6ULIRQ_NAME); /*class_creat(owner,name);*/
    if(IS_ERR(imx6ulirq.class)){   /*判断是否创建类成功*/
        ret = PTR_ERR(imx6ulirq.class);
        goto failed_class;
    }
 
    /*创建设备*/
 
    imx6ulirq.device = device_create(imx6ulirq.class, NULL, imx6ulirq.devid, NULL, IMX6ULIRQ_NAME);
     if(IS_ERR(imx6ulirq.device)){   /*判断是否创建类成功*/
        ret = PTR_ERR(imx6ulirq.device);
        goto failed_device;
    }
 
    /*初始化IO*/
    ret = keyio_init(&imx6ulirq);
    if(ret < 0) {
        goto failed_keyinit;
    }
 
    /*初始化原子变量*/
    atomic_set(&imx6ulirq.keyval, INVALKEY);
    atomic_set(&imx6ulirq.releasekey, 0);
 
 
    return 0;
 
failed_keyinit:
failed_device:
    class_destroy(imx6ulirq.class);
failed_class:
    cdev_del(&imx6ulirq.cdev);
failed_cdev:
    unregister_chrdev_region(imx6ulirq.devid, IMX6ULIRQ_COUNT);
failed_devid:
    return ret;
}
 
/*出口函数*/
static void __exit imx6ulirq_exit(void){
    int i = 0;
 
    /*释放中断*/
    for(i = 0;i< KEY_NUM;i++) {
        free_irq(imx6ulirq.irqkey[i].irqnum, &imx6ulirq); 
    }
    /*释放io*/
     for(i = 0;i< KEY_NUM;i++) {
        gpio_free(imx6ulirq.irqkey[i].gpio);
    }
    
    /*删除定时器*/
    del_timer_sync(&imx6ulirq.timer);
 
    /*注销字符设备*/
    cdev_del(&imx6ulirq.cdev);
    /*卸载设备*/
    unregister_chrdev_region(imx6ulirq.devid, IMX6ULIRQ_COUNT);
 
    device_destroy(imx6ulirq.class, imx6ulirq.devid);
    class_destroy(imx6ulirq.class);
 
}
 
 
/*模块入口和出口*/
module_init(imx6ulirq_init);
module_exit(imx6ulirq_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYC");

三:APP 

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <sys/ioctl.h>


int main(int argc, char *argv[])
{
    int fd, ret;
    char *filename;
    unsigned char data;


    if(argc != 2){
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];

    /* 打开 key 驱动 */
    fd = open(filename, O_RDWR);
    if(fd < 0){
        printf("file %s open failed!\r\n", argv[1]);
    return -1;
    }

    /*循环读取*/
    while(1){
        ret = read(fd, &data, sizeof(data));
        if(ret < 0){

        }
        else{
            if(data)
            printf("keyvalue = %#x \r\n", data);
        }
    }


    ret= close(fd); /* 关闭文件 */
    if(ret < 0){
        printf("file %s close failed!\r\n", argv[1]);
    return -1;
}
    return 0;
}

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux驱动开发中,中断是一种重要的机制。在Linux中,中断有两种类型:可抢占的内核代码运行在进程上下文中,而中断上下文则是不可被抢占的,会一直运行直到结束。对于硬件产生的中断,比如按键中断或网卡中断,被称为硬件中断,每个硬件中断都有对应的处理函数。在中断处理函数中,由于中断必须短暂且不能休眠,因此不能使用信号量或互斥锁,而是使用自旋锁来实现对共享资源的保护。此外,在Linux驱动开发中,可以使用计时函数来获取当前的系统时间。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [linux驱动开发-中断](https://blog.csdn.net/weixin_29898767/article/details/124320089)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* [关于Linux驱动开发的中断处理](https://blog.csdn.net/weixin_44468026/article/details/119763134)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值