Does the linux kernel have an interrupt function?

青灯夜游
Release: 2022-11-11 14:40:54
Original
2338 people have browsed it

The Linux kernel has an interrupt function. In the Linux kernel, if you want to use an interrupt, you need to apply for it, and the request_irq() function is used to apply for an interrupt. After the interrupt is used, the corresponding interrupt must be released through the free_irq() function; there are also enable_irq() and disable_irq( ), they are used to enable and disable specified interrupts.

Does the linux kernel have an interrupt function?

#The operating environment of this tutorial: linux7.3 system, Dell G3 computer.

1.Linux interrupt

1.1 Linux interrupt API function

request_irq function

In the Linux kernel, if you want to use an interrupt, you need to apply for it. The request_irq function is used to apply for an interrupt. The request_irq function may cause sleep, so the request_irq function cannot be used in interrupt context or other code segments that prohibit sleep. The request_irq function will activate (enable) the interrupt, so we do not need to manually enable the interrupt. The prototype of the request_irq function is as follows:
Does the linux kernel have an interrupt function?
irq: The interrupt number to apply for an interrupt.
handler: Interrupt processing function, this interrupt processing function will be executed when an interrupt occurs.
flags: Interrupt flags, you can view all interrupt flags in the file include/linux/interrupt.h
Does the linux kernel have an interrupt function?
name: Interrupt name, After setting, you can see the corresponding interrupt name in the /proc/interrupts file.
dev: If flags is set to IRQF_SHARED, dev is used to distinguish different interrupts. Generally, dev is set to the device structure, and dev will be passed to the second part of the interrupt processing function irq_handler_t parameter.
Return value: 0 means the interrupt application is successful, other negative values ​​mean the interrupt application fails. If -EBUSY is returned, it means that the interrupt has been applied for.

free_irq

When using an interrupt, you need to apply through the request_irq function. After the use is completed, the corresponding interrupt must be released through the free_irq function. If the interrupt is not shared, free_irq removes the interrupt handler and disables the interrupt. The free_irq function prototype is as follows:
Does the linux kernel have an interrupt function?
The meanings of the function parameters and return values ​​are as follows:
irq: The interrupt to be released.
dev: If the interrupt is set to shared (IRQF_SHARED), this parameter is used to distinguish specific interrupts. Shared interrupts will only be disabled when the last interrupt handler is released.
Return value: None.

Interrupt processing function

When using the request_irq function to apply for an interrupt, you need to set the interrupt processing function. The format of the interrupt processing function is as follows:
Does the linux kernel have an interrupt function?
Does the linux kernel have an interrupt function?

Interrupt enable and disable functions

Commonly used interrupt usage and disabling functions are as follows:
Does the linux kernel have an interrupt function?
enable_irq and disable_irq are used to enable and disable specified interrupts, irq is the interrupt number to be disabled. The disable_irq function will not return until the currently executing interrupt handling function is completed. Therefore, the user needs to ensure that no new interrupts will be generated and that all interrupt handlers that have started executing have exited. In this case, you can use another interrupt disabling function:
Does the linux kernel have an interrupt function?
disable_irq_nosync The function returns immediately after being called and does not wait for the current interrupt handler to complete execution. The above three functions all enable or disable a certain interrupt. Sometimes we need to turn off the entire interrupt system of the current processor, which is often said to turn off global interrupts when learning STM32. In this case, you can use the following two functions:
Does the linux kernel have an interrupt function?
local_irq_enable is used to enable the current processor interrupt system, and local_irq_disable is used to disable the current processor interrupt system. If task A calls local_irq_disable to turn off the global interrupt for 10S, task B starts running when it is turned off for 2S. Task B also calls local_irq_disable to turn off the global interrupt for 3S. After 3 seconds, task B calls the local_irq_enable function to turn on the global interrupt. At this time, 2 3 = 5 seconds have passed, and then the global interrupt is turned on. At this time, task A's desire to turn off the global interrupt for 10 seconds is shattered, and then task A becomes "angry". The result is very serious, maybe the system They will all be destroyed by Task A. In order to solve this problem, the B task cannot simply and crudely turn on the global interrupt through the local_irq_enable function, but restore the interrupt status to the previous state. Taking into account the feelings of other tasks, the following two functions must be used at this time. :
Does the linux kernel have an interrupt function?

1.2 The upper and lower halves

In some materials, the upper and lower halves are also called the top half. The bottom half and the bottom half mean the same thing. The interrupt service function we registered when using request_irq to apply for an interrupt belongs to the upper half of interrupt processing. As long as the interrupt is triggered, the interrupt processing function will be executed. We all know that the interrupt processing function must be executed as quickly as possible, and the shorter the better, but the reality is often cruel. Some interrupt processing processes are time-consuming, and we must process them to reduce the execution time of the interrupt processing function. For example, the capacitive touch screen notifies the SOC that a touch event occurs through an interrupt. The SOC responds to the interrupt, then reads the touch coordinate value through the IIC interface and reports it to the system. But we all know that the maximum speed of IIC is only 400Kbit/S, so reading data through IIC during interrupt will waste time. We can temporarily execute the operation of reading touch data through IIC, and the interrupt processing function only responds to the interrupt, and then clears the interrupt flag bit. At this time, the interrupt processing process is divided into two parts:
The upper part: The upper part is the interrupt processing function. Those processing processes are faster and the processing that does not take a long time can be completed in the upper part.
Lower half: If the interrupt processing process is more time-consuming, then these more time-consuming codes will be taken out and handed over to the lower half for execution, so that the interrupt processing function will fast in and out.
Therefore, the main purpose of the Linux kernel dividing interrupts into the upper half and the lower half is to realize fast in and fast out of the interrupt processing function. Those operations that are time-sensitive and fast can be placed in the interrupt processing function. That is the upper part. All the remaining work can be carried out in the lower half. For example, data is copied to the memory in the upper half, and the specific processing of the data can be carried out in the lower half. As for which codes belong to the upper half and which codes belong to the lower half, there are no clear regulations. All judgments are based on actual usage. This will test the skills of the driver writers. Here are some reference points that can be used for reference:
① If the content to be processed does not want to be interrupted by other interruptions, then it can be placed in the upper half.
②. If the task to be processed is time-sensitive, it can be placed in the upper half.
③. If the tasks to be processed are related to hardware, they can be placed in the upper half.
④. For tasks other than the above three points, priority should be placed in the lower half.
Lower half mechanism:

Soft interrupt

In the beginning, the Linux kernel provided a "bottom half" mechanism to implement the lower half, referred to as "BH". Later, soft interrupts and tasklets were introduced to replace the "BH" mechanism. Soft interrupts and tasklets can be used to replace BH. Starting from version 2.5 of the Linux kernel, BH has been abandoned. The Linux kernel uses the structure softirq_action to represent soft interrupts. The softirq_action structure is defined in the file include/linux/interrupt.h. The content is as follows:
Does the linux kernel have an interrupt function?
A total of 10 are defined in the kernel/softirq.c file Soft interrupt, as shown below:
Does the linux kernel have an interrupt function?
NR_SOFTIRQS is an enumeration type, defined in the file include/linux/interrupt.h, and defined as follows:
Does the linux kernel have an interrupt function?
As can be seen, There are 10 softirqs in total, so NR_SOFTIRQS is 10, so the array softirq_vec has 10 elements. The action member variable in the softirq_action structure is the service function of the soft interrupt. The array softirq_vec is a global array, so all CPUs (for SMP systems) can access it. Each CPU has its own triggering and control mechanism, and Only execute soft interrupts triggered by itself. However, the soft interrupt service functions executed by each CPU are indeed the same, and they are all action functions defined in the array softirq_vec. To use soft interrupts, you must first use the open_softirq function to register the corresponding soft interrupt processing function. The open_softirq function prototype is as follows:
Does the linux kernel have an interrupt function?
nr: The soft interrupt to be enabled is in sample code 51.1 Choose one from .2.3.
action: The processing function corresponding to the soft interrupt.
Return value: No return value.
After registering the soft interrupt, it needs to be triggered through the raise_softirq function. The prototype of the raise_softirq function is as follows:
Does the linux kernel have an interrupt function?
Soft interrupt must be statically registered at compile time! The Linux kernel uses the softirq_init function to initialize soft interrupts. The softirq_init function is defined in the kernel/softirq.c file. The function content is as follows:
Does the linux kernel have an interrupt function?

tasklet

Tasklet is another lower half mechanism implemented using soft interrupts. Between soft interrupts and tasklets, it is recommended that you use tasklets. The func function in line 489 of the Linux kernel structure
Does the linux kernel have an interrupt function?
is the processing function to be executed by the tasklet. The user-defined function content is equivalent to the interrupt processing function. If you want to use a tasklet, you must first define a tasklet, and then use the tasklet_init function to initialize the tasklet. The prototype of the taskled_init function is as follows:
Does the linux kernel have an interrupt function?
Does the linux kernel have an interrupt function?
The meanings of the function parameters and return values ​​are as follows:
t: The tasklet to be initialized
func: The processing function of the tasklet.
data: Parameters to be passed to the func function
Return value : No return value.
You can also use the macro DECLARE_TASKLET to complete the definition and initialization of tasklet at one time. DECLARE_TASKLET is defined in the include/linux/interrupt.h file and is defined as follows:
Does the linux kernel have an interrupt function?
where name is the name of the tasklet to be defined. , this name is a time variable of tasklet_struct type, func is the tasklet processing function, and data is the parameter passed to the func function.
In the upper part, that is, calling the tasklet_schedule function in the interrupt processing function can make the tasklet run at the appropriate time. The prototype of the tasklet_schedule function is as follows:
Does the linux kernel have an interrupt function?
The reference usage example of tasklet is as follows :
Does the linux kernel have an interrupt function?
Does the linux kernel have an interrupt function?

Work Queue

The work queue is another lower half execution method. The work queue executes in the process context. The work queue hands over the work to be postponed to a kernel thread for execution. Because the work queue works in the process context, the work Queues allow sleeping or rescheduling. Therefore, if the work you want to postpone can sleep, then you can choose the work queue, otherwise you can only choose soft interrupts or tasklets.
The Linux kernel uses the work_struct structure to represent a job, the content is as follows (conditional compilation is omitted):
Does the linux kernel have an interrupt function?
These jobs are organized into work queues, and the work queue is represented by the workqueue_struct structure, the content is as follows (omitted) Conditional compilation):
Does the linux kernel have an interrupt function?
The Linux kernel uses worker threads to process each job in the work queue. The Linux kernel uses the worker structure to represent the worker thread. The content of the worker structure is as follows:
Does the linux kernel have an interrupt function?
As can be seen from the sample code 51.1.2.10, each worker has a work queue, and the worker thread processes all the work in its own work queue. In actual driver development, we only need to define the work (work_struct), and we basically don't have to worry about the work queue and worker threads. Simply creating a work is very simple, just define a work_struct structure variable, and then use the INIT_WORK macro to initialize the work. The INIT_WORK macro is defined as follows:
Does the linux kernel have an interrupt function?
Does the linux kernel have an interrupt function?
Does the linux kernel have an interrupt function?

1.3 Device tree interrupt information node

If you use the device tree, you need to set the interrupt attribute information in the device tree. The Linux kernel reads the interrupt attribute information in the device tree. to configure interrupts. For interrupt controllers, refer to the document Documentation/devicetree/bindings/arm/gic.txt for device tree binding information. Open the imx6ull.dtsi file. The intc node is the interrupt controller node of I.MX6ULL. The node content is as follows:
Does the linux kernel have an interrupt function?
Line 2, the compatible attribute value is "arm,cortex-a7- gic" and search for "arm,cortex-a7-gic" in the Linux kernel source code to find the GIC interrupt controller driver file.
Line 3, #interrupt-cells is the same as #address-cells, #size-cells. Indicates the cell size of the device under this interrupt controller. For the device, the interrupts attribute is used to describe the interrupt information. #interrupt-cells describes the cells size of the interrupts attribute, that is, how many cells are there for one message. Each cell is a 32-bit integer value. For GIC processed by ARM, there are 3 cells in total. The meanings of these three cells are as follows:
The first cells: interrupt type, 0 means SPI interrupt, 1 means PPI interrupt.
The second cells: interrupt number. For SPI interrupts, the range of interrupt numbers is 0~987. For PPI interrupts, the range of interrupt numbers is 0~15.
The third cells: flag, bit[3:0] indicates the interrupt trigger type. When it is 1, it indicates a rising edge trigger. When it is 2, it indicates a falling edge trigger. When it is 4, it indicates a high level trigger. When 8, it means low level trigger. bit[15:8] is the CPU mask of PPI interrupt.
In line 4, the interrupt-controller node is empty, indicating that the current node is the interrupt controller.
For gpio, the gpio node can also be used as an interrupt controller. For example, the content of the gpio5 node in the imx6ull.dtsi file is as follows:
Does the linux kernel have an interrupt function?
Line 4, interrupts describes the interrupt source information, For gpio5, there are two pieces of information. The interrupt types are both SPI and the trigger levels are IRQ_TYPE_LEVEL_HIGH. The difference lies in the interrupt source, one is 74 and the other is 75. Open the "Chapter 3 Interrupts and DMA Events" chapter of the "IMX6ULL Reference Manual" and find Table 3-1, as shown in Figure 50.1.3.1:
Does the linux kernel have an interrupt function?
As can be seen from Figure 50.1.3.1, GPIO5 uses a total of 2 interrupt numbers, one is 74 and the other is 75. Among them, 74 corresponds to the lower 16 IOs of GPIO5_IO00~GPIO5_IO15, and 75 corresponds to the higher 16 IOs of GPIO5_IO16~GPIOI5_IO31. Line 8, interrupt-controller indicates that the gpio5 node is also an interrupt controller, used to control all IO
interrupts of gpio5.
Line 9, change #interrupt-cells to 2.
Open the imx6ull-alientek-emmc.dts file and find the following content:
Does the linux kernel have an interrupt function?
Does the linux kernel have an interrupt function?
Does the linux kernel have an interrupt function?

1.4 Get the interrupt number

You need to use the interrupt number when writing the driver. We use the interrupt number. The interrupt information has been written into the device tree, so the corresponding device number can be extracted from the interrupts attribute through the irq_of_parse_and_map function. The function prototype is as follows:
Does the linux kernel have an interrupt function?
The function parameters and return value have the following meanings:
dev: Device node.
index: Index number. The interrupts attribute may contain multiple pieces of interrupt information. Specify the information to be obtained through index.
Return value: Interrupt number.
If you use GPIO, you can use the gpio_to_irq function to get the interrupt number corresponding to gpio. The function prototype is as follows:
Does the linux kernel have an interrupt function?

2. Driver code

#include <linux/types.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/ide.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/errno.h> 
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <asm/mach/map.h>
#include <linux/timer.h>
#include <linux/jiffies.h>

#define IMX6UIRQ_CNT            1               /* 设备号个数 */
#define IMX6UIRQ_NAME           "irqDev"        /* 名字 */
#define KEY0VALUE               0X01            /* KEY0 按键值 */
#define INVAKEY                 0XFF            /* 无效的按键值 */
#define KEY_NUM                 1               /* 按键数量 */

/* 可能会有好多按键,通过结构体数组来描述按键 */
/* 中断 IO 描述结构体 */
struct irq_keydesc {
    int gpio;                               /* gpio */
    int irqnum;                             /* 中断号 */
    unsigned char value;                    /* 按键对应的键值 */
    char name[10];                          /* 名字 */
    irqreturn_t (*handler)(int, void *);    /* 中断服务函数 */
};

/* irq设备结构体 */
struct imx6uirq_dev {
    dev_t               devid;          /* 设备号 */
    struct cdev         cdev;           /* 字符设备 */
    struct class        *class;         /* 类 */
    struct device       *device;        /* 设备 */
    int                 major;          /* 注设备号 */
    int                 minor;          /* 次设备号 */
    struct device_node  *nd;            /* 设备节点 */

    atomic_t            keyvalue;       /* 有效的按键键值 */
    atomic_t            releasekey;     /* 标记是否完成一次完成的按键*/
    struct timer_list   timer;          /* 定义一个定时器*/
    struct irq_keydesc  irqkeydesc[KEY_NUM]; /* 按键描述数组 */
    unsigned char       curkeynum;      /* 当前的按键号 */
};

struct imx6uirq_dev  irqDev; /* 定义LED结构体 */

/* @description : 中断服务函数,开启定时器,延时 10ms,
* 定时器用于按键消抖。
* 两个参数是中断处理函数的必须写法
* @param - irq : 中断号
* @param - dev_id : 设备结构。
* @return : 中断执行结果
*/
static irqreturn_t key0_handler(int irq, void *dev_id)
{
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)dev_id;

    /* 采用定时器削抖,如果再定时器时间内还是这个值,说明是真的按下了,在定时器中断中处理 */
    /* 这里设置为0是简易处理,因为只有一个按键 */
    /* 有其他按键要再建一个中断处理函数,并把curkeynum改成相应的按键值 */
    /* 注意不能所有按键用一个中断函数,第一是一起按的时候会出错 */
    /* 第二,无法用curkeynum判断使用的是第几个按键 */
    dev->curkeynum = 0;
    /* 传递给定时器的参数,注意要强转,在中断处理函数里面再转回来 */
    dev->timer.data = (volatile long)dev_id;
    /* mod_timer会启动定时器,第二个参数是要修改的超时时间 */
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));
    return IRQ_RETVAL(IRQ_HANDLED);
}

 /* @description : 定时器服务函数,用于按键消抖,定时器到了以后
* 再次读取按键值,如果按键还是处于按下状态就表示按键有效。
* @param – arg : 设备结构变量
* @return : 无
*/
void timer_function(unsigned long arg)
{
    unsigned char value;
    unsigned char num;
    struct irq_keydesc *keydesc;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg; 

    /* 因为只有一个按键,这里是0 */
    num = dev->curkeynum;
    keydesc = &dev->irqkeydesc[num]; 
    value = gpio_get_value(keydesc->gpio); /* 读取 IO 值 */

    if(value == 0){ /* 按下按键 */
        atomic_set(&dev->keyvalue, keydesc->value);
    }
    else{ /* 按键松开 */
        /* 这种情况是按下再松开的松开,使用keyValue加上releaseKey */
        /* 没按下的话, releasekey一直为0*/
        atomic_set(&dev->keyvalue, 0x80 | keydesc->value);
        atomic_set(&dev->releasekey, 1); /* 标记松开按键 */ 
    } 
}

/*
* @description : 按键 IO 初始化
* @param : 无
* @return : 无
*/
static int keyio_init(void)
{
    unsigned char i = 0;

    int ret = 0;

    /* 1.获取key节点 */
    irqDev.nd = of_find_node_by_path("/key");
    if (irqDev.nd== NULL){
        printk("key node not find!\r\n");
        return -EINVAL;
    }
    /* 对每个按键都提取 GPIO */
    for (i = 0; i < KEY_NUM; i++) {
        irqDev.irqkeydesc[i].gpio = of_get_named_gpio(irqDev.nd, "key-gpios", i);
        if (irqDev.irqkeydesc[i].gpio < 0) {
            printk("can&#39;t get key%d\r\n", i);
        }
    }

    /* 初始化 key 所使用的 IO,并且设置成中断模式 */
    for (i = 0; i < KEY_NUM; i++) {
        /* 先对每一个IO命名 */
        /* 先对命名清0 */
        memset(irqDev.irqkeydesc[i].name, 0, sizeof(irqDev.irqkeydesc[i].name)); 
        /* 给IO命名 */
        sprintf(irqDev.irqkeydesc[i].name, "KEY%d", i); 
        /* 请求GPIO */
        gpio_request(irqDev.irqkeydesc[i].gpio, irqDev.irqkeydesc[i].name);
        
        /* 设置GPIO为输入 */
        gpio_direction_input(irqDev.irqkeydesc[i].gpio); 

        /* 获取中断号,以下为两个方法,都可以获取到 */
        /* 从interrupts属性里面获取 */
        /* 注意i是根据设备树里面设置了多少个就是多少个,都会获取到 */
        /* 下面的方法是通用的获取中断号的函数 */
        irqDev.irqkeydesc[i].irqnum = irq_of_parse_and_map(irqDev.nd, i);
#if 0
        /* 此方法是gpio获取中断号的方法 */
        irqDev.irqkeydesc[i].irqnum = gpio_to_irq(irqDev.irqkeydesc[i].gpio);
#endif
        printk("key%d:gpio=%d, irqnum=%d\r\n", i, irqDev.irqkeydesc[i].gpio,
                                                  irqDev.irqkeydesc[i].irqnum);
    }

    /* 2. 按键中断初始化 */
    /* 设置中断处理函数和按键初始值 */
    /* 因为只有一个key0.,所以这里也没用循环 */
    irqDev.irqkeydesc[0].handler = key0_handler;
    irqDev.irqkeydesc[0].value = KEY0VALUE;
     /* 申请中断 */
    for (i = 0; i < KEY_NUM; i++) {
        /* request_irq参数
         * 中断号,中断函数,中断触发类型,中断名字,传递给中断处理函数的参数(第二个),这里传的结构体
         * */
        ret = request_irq(irqDev.irqkeydesc[i].irqnum, irqDev.irqkeydesc[i].handler,
                        IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
                        irqDev.irqkeydesc[i].name, &irqDev);
        if(ret < 0){
            printk("irq %d request failed!\r\n", irqDev.irqkeydesc[i].irqnum);
            return -EFAULT;
        }
    }

    /* 3. 创建定时器 */
    init_timer(&irqDev.timer);
    irqDev.timer.function = timer_function;
    /* 注意下面不能让定时器运行,因为要按下按键之后再运行 */
    /* 启动定时器通过mod_timer启动,通常在初始化阶段的定时器用的是add_timer */
    return 0;
}


static int imx6uirq_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &irqDev;
    return 0;
}

static int imx6uirq_release(struct inode *inode, struct file *filp)
{
    //struct imx6uirq_dev  *dev = (struct imx6uirq_dev  *)filp->private_data;
    return 0;
}

 /*
* @description : 从设备读取数据
* @param – filp : 要打开的设备文件(文件描述符)
* @param – buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param – offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    int ret = 0;
    unsigned char keyvalue = 0;     /* 按键值 */
    unsigned char releasekey = 0;   /* 标记是否一次完成 */
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;

    keyvalue = atomic_read(&dev->keyvalue);
    releasekey = atomic_read(&dev->releasekey);

    if (releasekey) { /* 有按键按下 */
        if (keyvalue & 0x80) {
            keyvalue &= ~0x80; /* 因为中断中或了一个0x80,这里面去掉0x80 */
            ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
        } else {
            goto data_error;
        }
        atomic_set(&dev->releasekey, 0); /* 按下标志清零 */
    } else { /* 没有按下 */
        goto data_error;
    }
    return 0;

data_error:
    return -EINVAL;
}

/* 字符设备操作集 */
static const struct file_operations imx6uirq_fops = {
    .owner = THIS_MODULE,
    .open = imx6uirq_open,
    .release = imx6uirq_release,
    .read = imx6uirq_read
};

/* 模块入口函数 */
static int __init imx6uirq_init(void)
{
    /* 定义一些所需变量 */
    int ret = 0;

    /* 1. 注册字符设备驱动 */
    irqDev.major = 0;
    if(irqDev.major) {
        irqDev.devid = MKDEV(irqDev.major, 0);
        ret = register_chrdev_region(irqDev.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME );  
    } else {
        alloc_chrdev_region(&irqDev.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME );
        irqDev.major = MAJOR(irqDev.devid);
        irqDev.minor = MINOR(irqDev.devid);
    }
    if(ret < 0){
        goto fail_devid;
    }
    printk("Make devid success! \r\n");
    printk("major = %d, minor = %d \r\n", irqDev.major, irqDev.minor);

    /* 2. 初始化cdev */
    irqDev.cdev.owner = THIS_MODULE;
    cdev_init(&irqDev.cdev, &imx6uirq_fops);
    ret = cdev_add(&irqDev.cdev, irqDev.devid, IMX6UIRQ_CNT);
    if (ret < 0){
        goto fail_cdev;
    } else {
        printk("Cdev add sucess! \r\n");
    }

    /* 3. 自动创建设备节点 */
    irqDev.class = class_create(THIS_MODULE, IMX6UIRQ_NAME );
    if(IS_ERR(irqDev.class)) {
        ret = PTR_ERR(irqDev.class);
        goto fail_class;
    } else {
        printk("Class create sucess! \r\n");
    }

    irqDev.device = device_create(irqDev.class, NULL, irqDev.devid, NULL, IMX6UIRQ_NAME );
    if(IS_ERR(irqDev.device)) {
        ret = PTR_ERR(irqDev.device);
        goto fail_device;
    } else {
        printk("Device create sucess! \r\n");
    }

    /* 4.初始化按键 */
    atomic_set(&irqDev.keyvalue, INVAKEY);
    atomic_set(&irqDev.releasekey, 0);
    keyio_init();

    printk("irqDev init! \r\n");
    return 0;

/* 错误处理 */
fail_device:
    class_destroy(irqDev.class);
fail_class:
    cdev_del(&irqDev.cdev);
fail_cdev:
    unregister_chrdev_region(irqDev.devid, IMX6UIRQ_CNT);
fail_devid:
    return ret;
}

/* 模块出口函数 */
static void __exit imx6uirq_exit(void)
{
    unsigned int i = 0;
    /* 删除定时器 */
    del_timer_sync(&irqDev.timer); 

    /* 释放中断 */
    for (i = 0; i < KEY_NUM; i++) {
        free_irq(irqDev.irqkeydesc[i].irqnum, &irqDev);
    }

    /* 1. 释放设备号 */
    cdev_del(&irqDev.cdev);
    /* 2. 注销设备号 */
    unregister_chrdev_region(irqDev.devid, IMX6UIRQ_CNT);
    /* 3. 摧毁设备 */
    device_destroy(irqDev.class, irqDev.devid);
    /* 4.摧毁类 */
    class_destroy(irqDev.class);


    printk("irqDev exit! \r\n");
}

/* 模块入口和出口注册 */
module_init(imx6uirq_init);
module_exit(imx6uirq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Shao Zheming");
Copy after login

3. Application code

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

/* 
 * argc: 应用程序参数个数
 * argv[]: 参数是什么,具体的参数,说明参数是字符串的形式
 * .chrdevbaseApp <filename> <0:1> 0表示关灯,1表示开灯
 * .chrdevbaseApp /dev/led 0 关灯
 * .chrdevbaseApp /dev/led 1 开灯
 * */


int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        printf("Error Usage!\r\n");
        return -1;
    }
    
    int fd, ret;
    char *filename;
    unsigned char data;

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

    while (1) {
        ret = read(fd, &data, sizeof(data));
        if (ret < 0) { /* 数据读取错误或者无效 */
        
        } else { /* 数据读取正确 */
            if (data) /* 读取到数据 */
                printf("key value = %#X\r\n", data);
        }
    }

    close(fd);
    return 0;
}
Copy after login
Copy after login

4. Use tasklet to handle the second half of the interrupt

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

/* 
 * argc: 应用程序参数个数
 * argv[]: 参数是什么,具体的参数,说明参数是字符串的形式
 * .chrdevbaseApp <filename> <0:1> 0表示关灯,1表示开灯
 * .chrdevbaseApp /dev/led 0 关灯
 * .chrdevbaseApp /dev/led 1 开灯
 * */


int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        printf("Error Usage!\r\n");
        return -1;
    }
    
    int fd, ret;
    char *filename;
    unsigned char data;

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

    while (1) {
        ret = read(fd, &data, sizeof(data));
        if (ret < 0) { /* 数据读取错误或者无效 */
        
        } else { /* 数据读取正确 */
            if (data) /* 读取到数据 */
                printf("key value = %#X\r\n", data);
        }
    }

    close(fd);
    return 0;
}
Copy after login
Copy after login

5. Work queue processing The lower part

The development method is the same as tasklet
Note that work can deduce the device dev structure, so work is generally placed in the dev structure

Related recommendations: "Linux Video Tutorial"

The above is the detailed content of Does the linux kernel have an interrupt function?. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!