SylixOS 共用中断号机制

1.原理概述

SylixOS开发人员在编写中断驱动时经常会遇到多个中断源共用一个中断号的情况,但在驱动中为了保证代码独立性,各个外设的中断服务函数应该放在各自的C文件中编写,用统一的中断服务函数是不合理的。为了适应这种情况,SylixOS支持队列类型中断向量,即SylixOS内核将同一中断向量号的多个中断服务函数链接成队列,执行时依次执行。

2.技术实现

2.1应用举例

下面以at91sam9x25处理器为例,该处理器的调试串口,tick时钟等外设共用1号中断向量。如图2-1所示。

图2-1 中断向量

中断向量详见AT91SAM9X25芯片手册8.2节Peripheral Identifiers部分。

2.2代码分析

SylixOS开发人员需要将1号中断向量设置成队列类型中断向量,设置方法如程序清单1所示。

程序清单 1 中断初始化

VOID  bspIntInit (VOID)
{
    interruptInit();                                                    /*  中断控制器初始化            */
    API_InterVectorSetFlag(ID_SYS, LW_IRQ_FLAG_QUEUE);                  /*  设置ID_SYS为单向量、多服务  */
}

bspIntInit函数位于bspLib.c中,用来初始化中断控制器。

ID_SYS是1号中断向量,LW_IRQ_FLAG_QUEUE宏是队列类型中断向量的标志,该标志必须在安装任何一个驱动前设置, 且设置后不能再取消,因此应该在 bspIntInit函数中完成设置。

API_InterVectorSetFlag函数用来设置中断向量属性,代码实现如程序清单2所示。

程序清单 2 中断向量属性设置函数

LW_API
ULONG  API_InterVectorSetFlag (ULONG  ulVector, ULONG  ulFlag)
{
    INTREG              iregInterLevel;
    PLW_CLASS_INTDESC   pidesc;

    if (_Inter_Vector_Invalid(ulVector)) {
        _ErrorHandle(ERROR_KERNEL_VECTOR_NULL);
        return  (ERROR_KERNEL_VECTOR_NULL);
    }
    
    pidesc = LW_IVEC_GET_IDESC(ulVector);
    
    LW_SPIN_LOCK_QUICK(&pidesc->IDESC_slLock, &iregInterLevel);         /*  关闭中断同时锁住 spinlock   */
    
    if (LW_IVEC_GET_FLAG(ulVector) & LW_IRQ_FLAG_QUEUE) {               /*  已经是 QUEUE 类型中断向量   */
        LW_IVEC_SET_FLAG(ulVector, ulFlag | LW_IRQ_FLAG_QUEUE);
    
    } else {
        LW_IVEC_SET_FLAG(ulVector, ulFlag);                             /*  中断向量选项设置            */
    }
    
    LW_SPIN_UNLOCK_QUICK(&pidesc->IDESC_slLock, iregInterLevel);        /*  打开中断, 同时打开 spinlock */
    
    return  (ERROR_NONE);
}

设置完成后,不同外设多次调用API_InterVectorConnect 函数设置1号中断向量的多个中断服务函数,以AT91SAM9X25 调试串口中断服务函数和tick时钟中断服务函数为例,如程序清单3、程序清单4所示。

程序清单 3 tick中断服务函数安装

   API_InterVectorConnect(ulVector,
                       (PINT_SVR_ROUTINE)__tickTimerIsr,                /*  tick中断服务函数            */
                       LW_NULL,
                       "tick_timer");

程序清单 4  串口中断服务函数安装

    API_InterVectorConnect(sam9x25UartIntNum(iChannelNum),
                           (PINT_SVR_ROUTINE)sam9x25SioIsr,             /*  串口中断服务函数安装        */
                           (PVOID)psiochanUart,
                           pcIsrName);

Tick和串口中断向量号均为1。

API_InterVectorConnect函数将中断向量号和中断服务函数进行挂接时,会判断中断向量是否是队列类型中断向量,若是队列类型中断向量,则将中断服务函数加入中断队列,部分代码实现如程序清单5所示。

程序清单 5 中断服务函数挂接

LW_API
ULONG  API_InterVectorConnectEx (ULONG              ulVector,
                                 PINT_SVR_ROUTINE   pfuncIsr,
                                 VOIDFUNCPTR        pfuncClear,
                                 PVOID              pvArg,
                                 CPCHAR             pcName)
{
    ... ...
    if (LW_IVEC_GET_FLAG(ulVector) & LW_IRQ_FLAG_QUEUE) {               /*  队列服务类型向量            */
        for (plineTemp  = pidesc->IDESC_plineAction;
             plineTemp != LW_NULL;
             plineTemp  = _list_line_get_next(plineTemp)) {
             
            piactionOld = _LIST_ENTRY(plineTemp, LW_CLASS_INTACT, IACT_plineManage);
            if ((piactionOld->IACT_pfuncIsr == pfuncIsr) &&
                (piactionOld->IACT_pvArg    == pvArg)) {                /*  中断处理函数是否被重复安装  */
                break;
            }
        }
        
        if (plineTemp) {                                                /*  此中断被重复安装            */
            bNeedFree = LW_TRUE;
        
        } else {
            /*
             *  将中断服务函数加入到对应的中断向量号
             *  下的中断服务函数列表
             */

            _List_Line_Add_Ahead(&piaction->IACT_plineManage,
                                 &pidesc->IDESC_plineAction);
            bNeedFree = LW_FALSE;
        }
    
} else {                                                            /*  非队列服务式中断向量        */
    ... ...
}

当产生中断并执行中断服务函数时,SylixOS内核总中断服务函数API_InterVectorIsr会找到对应中断号的中断服务函数链表,并遍历链表,部分代码如程序清单6所示。

程序清单 6 总中断服务函数

LW_API
irqreturn_t  API_InterVectorIsr (ULONG  ulVector)
{
    ... ...
        for (plineTemp  = pidesc->IDESC_plineAction;
             plineTemp != LW_NULL;
             plineTemp  = _list_line_get_next(plineTemp)) {
            
            piaction = _LIST_ENTRY(plineTemp, LW_CLASS_INTACT, IACT_plineManage);
            {
                irqret = piaction->IACT_pfuncIsr(piaction->IACT_pvArg, ulVector);
            }
            
            /*
             *  根据中断服务函数返回值判断,中断
             *  是否被处理。返回值为1时表示被处理
             *  跳出循环;为0时表示未被处理,继续
             *  遍历服务函数链表。
             */
            if (LW_IRQ_RETVAL(irqret)) {                                /*  中断是否已经被处理          */
                piaction->IACT_iIntCnt[pcpu->CPU_ulCPUId]++;
                if (piaction->IACT_pfuncClear) {
                    piaction->IACT_pfuncClear(piaction->IACT_pvArg, ulVector);
                }
                break;                                                  /*  跳出循环                    */
            }
        }
       ... ...
}

由程序清单6可知,当中断向量号是队列类型中断向量时,中断服务函数返回值显得尤为重要,为0时表示中断函数未被处理,返回值为1时表示中断函数已经被处理,所以串口中断服务函数编写如程序清单7所示。

程序清单 7 串口中断函数

static irqreturn_t  sam9x25SioIsr (SIO_CHAN  *psiochanChan, ULONG  ulVector)
{
    ... ...
    /*
     *  得出具体是哪一个串口中断
     */
    uiStatus  = readl(DBGU_BA + REG_US_CSR);
    uiPending = uiStatus & readl(DBGU_BA + REG_US_IMR);

    if(uiPending != 0) {                                                      /*  串口中断              */
        if (sam9x25UartIsTxInt(uiPending)) {                                  /*  发送中断              */
            if (psiochanUart->pcbGetTxChar(psiochanUart->pvGetTxArg, &cChar)
                    != ERROR_NONE) {
                sam9x25UartTxIntDisable(psiochanUart->iChannelNum);           /*  关闭发送中断          */

            } else {
                sam9x25UartTxPut(psiochanUart->iChannelNum, cChar);           /*  发送数据              */
            }
            return  (LW_IRQ_HANDLED);
        }

        if (sam9x25UartIsRxInt(uiPending)) {                                  /*  接收中断              */
            if (sam9x25UartRxGet(psiochanUart->iChannelNum, &cChar)           /*  接收数据              */
                    == ERROR_NONE) {
                psiochanUart->pcbPutRcvChar(psiochanUart->pvPutRcvArg, cChar);
            } else {

            }

            /*
             *  返回值是1,表示是串口中断,中断处理结束
             */
            return  (LW_IRQ_HANDLED);
        }
    }
    /*
     *  返回值是0,表示不是串口中断,继续遍历中断服务函数链表
     */
    return  (LW_IRQ_NONE);
}

Tick中断服务函数编写如程序清单8所示。

程序清单 8 tick中断函数

static irqreturn_t  __tickTimerIsr (VOID)
{
    INT uiPending;

    uiPending = readl(REG_PIT_SR);

    if (uiPending & 1) {                                                    /*  tick 中断                   */
        API_KernelTicksContext();                                           /*  保存被时钟中断的线程控制块  */
        API_KernelTicks();                                                  /*  内核 TICKS 通知             */
        API_TimerHTicks();                                                  /*  高速 TIMER TICKS 通知       */

        timerIsr();

        return  (LW_IRQ_HANDLED);                                           /*  tick中断,中断结束          */
    }
    /*
     *  不是tick中断,继续遍历中断服务函数
     */
    return  (LW_IRQ_NONE);
}

SylixOS开发人员在编写单向量、多中断中断服务函数时,中断服务函数中应该首先读取具体外设的状态寄存器以判断是否是该外设产生的中断,否则会进入其他外设中断,结束遍历中断服务函数链表,导致错误。

时间: 2024-09-29 21:12:41

SylixOS 共用中断号机制的相关文章

Linux x86_64 APIC中断路由机制分析

不同CPU体系间的中断控制器工作原理有较大差异,本文是<Linux mips64r2 PCI中断路由机制分析>的姊妹篇,主要分析Broadwell-DE X86_64 APIC中断路由原理.中断配置和处理过程,并尝试回答如下问题: 为什么x86中断路由使用IO-APIC/LAPIC框架,其有什么价值? pin/irq/vector的区别.作用,取值范围和分配机制? x86_64 APIC关键概念 Pin 此处的pin特指APIC的中断输入引脚,与内外部设备的中断输入信号相连.从上图中可以看出,

Linux mips64r2 PCI中断路由机制分析

Linux mips64r2 PCI中断路由机制分析 本文主要分析mips64r2 PCI设备中断路由原理和irq号分配实现方法,并尝试回答如下问题: PCI设备驱动中断注册(request_irq)时的irq#从哪里来?是硬件相关?还是软件相关? 中断上报时,CPU是如何获得这个irq#的? 本文主要分析PIC(可编程中断控制器)的工作原理,PIC一般集成在CPU中,不同arch.vendor CPU的PIC实现原理也不尽相同.本文基于kerne3.10 + mips64r2 XXX CPU分

内核中断号必需要跟硬件中断号一致吗

首先说明.答案是否定的,内核中断号能够与硬件中断号不一致.可是这是个无聊的问题.. 有用价值不大.可是却能够引起对内核软件中断号与硬件中断号关系的思考. 两者的关系我认为能够从中断的初始化和分发过程来一探到底. 这里就从ARM PPC MIPS 3款主流嵌入式处理器架构的内核代码框架中来分析下他们中断的初始化和分发过程. 一 中断的初始化 对于中断初始化,在系统启动过程中,这3款处理器架构的内核软件框架中都会有对应的中断初始化函数. 内核启动函数start_kernel中会调用init_IRQ来

内核中断号必须要跟硬件中断号一致吗

首先说明,答案是否定的,内核中断号可以与硬件中断号不一致,但是这是个无聊的问题..实用价值不大.但是却可以引起对内核软件中断号与硬件中断号关系的思考. 两者的关系我觉得可以从中断的初始化和分发过程来一探究竟. 这里就从ARM PPC MIPS 3款主流嵌入式处理器架构的内核代码框架中来分析下他们中断的初始化和分发过程. 一 中断的初始化 对于中断初始化,在系统启动过程中,这3款处理器架构的内核软件框架中都会有相应的中断初始化函数. 内核启动函数start_kernel中会调用init_IRQ来进

linux中断子系统:中断号的映射与维护

写在前沿: 好久好久没有静下心来整理一些东西了,开始工作已有一个月,脑子里想整理的东西特别多.记录是一种很好的自我学习方式,静下来多思考多总结,三年的工作目标不能发生变化,作为职场菜鸟即将进入全世界半导体第一的Intel working,是机遇更是一种挑战,困难也是可想而知.脚踏实地.仰望星空,以结果为导向,以目标为准则,争取每天进步一点点. Linux内核版本:3.4.39 一. linux中断子系统的irq_desc初始化 linux内核最初的中断初始化过程入口为start_kernel.在

linux设备驱动归纳总结(六):2.分享中断号【转】

linux设备驱动归纳总结(六):2.分享中断号 转自:http://blog.chinaunix.net/uid-25014876-id-90837.html xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 上一节介绍的内容是,调用接口request_irq(),使中断号与中断处理函数对应.但是,有时候会有这样的情况,如果开发板上按键的中断已经被另外的驱动程序注册中断了

Linux-信号机制详解(一)

之前有写过SystemV的信号量机制,现在是信号.这里的信号和前面的信号量是不同的.这里的信号是进程给操作系统或进程的某种信息,让操作系统或者其他进程做出某种反应. 信号是进程间通信机制中唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达.进程之间可以互相通过系统调用kill发送软中断信号.内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件.信号机制除了基本通知功能外,还可以传递附加信息. 在这里举几个常见的信号的例子: 1. 用户

s3c6410外部中断号与引脚问题

在编写Linux驱动程序时,我想用某一个引脚做外部中断.但是在调用 int request_irq(unsigned int irq, void (*handler)(int irq, void *dev_id, struct pt_regs *regs ), unsigned long irqflags, const char * devname, void *dev_id); 这个函数的时候遇到了一个问题,irq是个硬件终端号,但是到底这个引脚对应的终端号是多少呢? 我在网上找资料说的都稀里

SylixOS x86中断探测(二)

MP Spec简介 MP Spec即MultiProcessor Specification,简称MPS,中文翻译为多重处理器规范,定义了MP系统配置的数据结构.BIOS构建MP配置数据结构,将硬件以已知格式呈现给标准设备驱动程序或操作系统的硬件抽象层.该规范的总体概念如图 1.1所示. 图 1.1 总体概念 MP Spec提供了两种将信息传递给操作系统的方法:一种符合一组常见硬件默认设置的最小配置方法,以及一种在硬件设计中提供最大灵活性的最大方法.图 1.2显示了MP配置数据结构的总体布局.