Linux 中断实验

一.什么是中断

中断是指 CPU 在执行程序的过程中,出现了某些突发事件急待处理, CPU 必须暂停当前程序的执行,转去处理突发事件,处理完毕后又返回原程序被中断的位置继续执行。由于中断的存在极大的提高了 CPU 的运行效率,但是设备的中断会打断内核进程中的正常调度和运行,系统对更高吞吐率的追求势必要求中断服务程序尽量短小精悍。
举例来说,我现在正在厨房做饭,突然电话响了,然后我关火去接电话,接完电话在回去开火继续做饭,这个过程就是中断的一个过程。

二.中断相关函数

linux 中断有专门的中断子系统,其实现原理很复杂,但是驱动开发者不需要知道其实现的具体细节, 只需要知道如何应用该子系统提供的 API 函数来编写中断相关驱动代码即可。

1****获取中断号相关函数

编写驱动的时候需要用到中断号,每一个中断都有中断号,我们用到中断号,中断信息已经写到了设 备树里面,因此可以通过 irq_of_parse_and_map 函数从 interupts 属性中提取到对应的设备号。

2****申请中断函数

同 GPIO 一样,在 Linux 内核里面,如果我们要使用某个中断也是需要申请的,申请中断我们使用的函数是 request_irq 。

中断标识可以在文件 include/linux/interrupt.h 里面查看所有的中断标志,这里我们介绍几个常用的中断标志,如下图所示:

|----------------------|-----------------------------------------------------------------------------|
| 标志 | 功能 |
| IRQF_SHARED | 多个设备共享一个中断线,共享的所有中断都必须指 定此标志。如果使用共享中断的话, request_irq 函数的 dev 参数就是唯一区分他们的标志。 |
| IRQF_ONESHOT | 单次中断,中断执行一次就结束。 |
| IRQF_TRIGGER_NONE | 无触发。 |
| IRQF_TRIGGER_RISING | 上升沿触发。 |
| IRQF_TRIGGER_FALLING | 下降沿触发。 |
| IRQF_TRIGGER_HIGH | 高电平触发。 |
| IRQF_TRIGGER_LOW | 低电平触发。 |

3**、free_irq函数**

中断使用完成以后就要通过 free_irq 函数释放掉相应的中断。如果中断不是共享的,那么 free_irq 会删除中断处理函数并且禁止中断。free_irq 函数原型如下所示

4**、中断处理函数**

使用 request_irq 函数申请中断的时候需要设置中断处理函数,中断处理函数函数如下表所示:

三.设备树中的中断节点

cs 复制代码
1 gpio5 : gpio @020ac000{
2 compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
3 reg = <0x020ac000 0x4000>;
4 interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
5 gpio-controller;
6 #gpio-cells = <2>;
7 interrupt-controller;
8 #interrupt-cells = <2>;
9 };

第 4 行,interrupts 描述中断源信息,对于 gpio5 来说一共有两条信息,中断类型都是 SPI,触发电平都是 IRQ_TYPE_LEVEL_HIGH。不同之处在于中断源,一个是 74,一个是 75。

四.代码案例

cs 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
//定义结构体表示我们的节点
struct device_node *test_device_node;
struct property *test_node_property;
//要申请的中断号
int irq;
// GPIO 编号
int gpio_nu;
/*** @description: 中断处理函数 test_key
* @param {int} irq :要申请的中断号
* @param {void} *args :
* @return {*}IRQ_HANDLED
*/
irqreturn_t test_key(int irq, void *args)
{
    printk("test_key \n");
    return IRQ_RETVAL(IRQ_HANDLED);
}
/******************************************************************************
* @brief beep_probe : 与设备信息层(设备树)匹配成功后自动执行此函数,
* @param inode : 文件索引
* @param file : 文件
* @return 成功返回 0
******************************************************************************/
int beep_probe(struct platform_device *pdev)
{
    int ret = 0;
    // 打印匹配成功进入 probe 函数
    printk("beep_probe\n");
    // of_find_node_by_path 函数通过路径查找节点,/test_key 是设备树下的节点路径
    test_device_node = of_find_node_by_path("/test_key");
    if (test_device_node == NULL)
    {
        //查找节点失败则打印信息
        printk("of_find_node_by_path is error \n");
        return -1;
    }
    //of_get_named_gpio 函数获取 GPIO 编号
    gpio_nu = of_get_named_gpio(test_device_node, "gpios", 0);
    if (gpio_nu < 0)
    {
        printk("of_get_namd_gpio is error \n");
        return -1;
    }
    //设置 GPIO 为输入模式
    gpio_direction_input(gpio_nu);
    //获取 GPIO 对应的中断号
    // irq = gpio_to_irq(gpio_nu);
    irq =irq_of_parse_and_map(test_device_node,0);
    printk("irq is %d \n", irq);
    /*申请中断,irq:中断号名字
    test_key:中断处理函数
    IRQF_TRIGGER_RISING:中断标志,意为上升沿触发
    "test_key":中断的名字
    */
    ret = request_irq(irq, test_key, IRQF_TRIGGER_RISING, "test_key", NULL);
    if (ret < 0)
    {
        printk("request_irq is error \n");
        return -1;
    }
    return 0;
}
int beep_remove(struct platform_device *pdev)
{
    printk("beep_remove\n");
    return 0;
}
const struct platform_device_id beep_idtable = {
    .name = "keys",
};
const struct of_device_id of_match_table_test[] = {
    {.compatible = "keys"},
    {},
};
struct platform_driver beep_driver = {
    //3. 在 beep_driver 结构体中完成了 beep_probe 和 beep_remove
    .probe = beep_probe,
    .remove = beep_remove,
    .driver = {
                .owner = THIS_MODULE,
                .name = "beep_test",
                .of_match_table = of_match_table_test},
                //4 .id_table 的优先级要比 driver.name 的优先级要高,优先与.id_table 进行匹配
                .id_table = &beep_idtable};
/**
* @description: 模块初始化函数
* @param {*}
* @return {*}
*/
static int beep_driver_init(void)
{
    //1.我们看驱动文件要从 init 函数开始看
    int ret = 0;
    //2.在 init 函数里面注册了 platform_driver
    ret = platform_driver_register(&beep_driver);
    if (ret < 0)
    {
        printk("platform_driver_register error \n");
    }
    printk("platform_driver_register ok \n");
    return 0;
}
/**
* @description: 模块卸载函数
* @param {*}
* @return {*}
*/
static void beep_driver_exit(void)
{
    free_irq(irq, NULL);
    platform_driver_unregister(&beep_driver);
    printk("gooodbye! \n");
}
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");
相关推荐
cui_win几秒前
【网络】Linux 内核优化实战 - net.ipv4.tcp_dsack
linux·网络·tcp/ip
weixin_4567325913 分钟前
Live555-RTSP服务器
运维·服务器
别猜别猜了2 小时前
Linux操作系统02
linux·运维·服务器
C++ 老炮儿的技术栈3 小时前
Visual Studio 2022 MFC Dialog 添加Toolbar及Tips提示
服务器·c语言·数据库·c++·ide·算法·visual studio
阿巴~阿巴~3 小时前
实战Linux进程状态观察:R、S、D、T、Z状态详解与实验模拟
linux·运维·服务器
天高云淡ylz3 小时前
各类电子设备镜像格式及文件系统统计
linux·windows·risc-v
vivo互联网技术3 小时前
vivo Pulsar 万亿级消息处理实践(3)-KoP指标异常修复
java·大数据·服务器·后端·kafka·消息队列·pulsar
南川琼语3 小时前
Linux——I/O复用
linux·运维·服务器
cui_win3 小时前
【网络】Linux 内核优化实战 - net.ipv4.tcp_ecn
linux·网络·tcp/ip
柳如烟@3 小时前
零基础,使用Idea工具写一个邮件报警程序
java·服务器·前端