Marvell xcat 系列交换芯片Linux下的中断机制

Marvell xCat 系列交换芯片中断机制

--by handawei :QQ:1328990789

   Marvell Xcat 系列交换芯片作为FE/GE/XE交换机的主方案来讲,性价比很高。

本文以AC3 : 98DX3236 作为对象,介绍CPSS SDK中断机制。

AC3 交换芯片内部有一个ARM V7 双核800MHz 的CPU。 因此产品不需要额外的处理器。整个方案功耗方面做得很小。

有两路serdes可以作为连接到CPU内部,当CPU的管理网口,也可以作为连接PP的业务口。

AC3 serdes资源如下:



如果出电口的话, 推荐方案使用88E1685。光口则可以使用Serdes 直接引出。如果想做Combo,推荐方案88E1548 。都可以接2路QSGMII,出8个口。

与Drake(BC2系列)不同,  AC3 的内部只有一个包处理器(简称PP)。整体最多出24个GE,4个XE。 PP作为内部交换芯片,中断部分也直接连到CPU内部,而不是像Lion/Lion2 作为PCIE从设备,通过PCIE中断进入CPU。

下面是AC3 部分中断源信息:


       可以看到, 中断33 是第一个switch core的中断号。 AC3内部的PP的中断号就是33。但在CPSS SDK中,这个资源并不是直接写在代码中(兼容性)。也不是是像其他Linux下的platform设备一样,使用platform_get_resource(pdev, IORESOURCE_IRQ,0) 这种方式获。而是将PP作为一个PCIE设备,读取其PCIE配置空间的头部信息,获取到中断号。但PP并不是作为PCIE从设备,显式地挂载到PCIE总线下,因此在Linux 下,并不能通过cat /proc/bus/pci/devices 这种方式直接看到PP设备的存在,只能通过在内核态下对PCIE总线进行遍历,匹配到设备ID之后,再按照配置空间的头部的固定格式将中断号解析出来。

   

      拿到中断号后,再交由内核态的通过request_irq进行中断服务函数的注册。在中断来临后,该中断服务函数调用一个tasklet软中断,软中断再触发事件对应的监听线程。在事件监听线程里面,通过对具体事件枚举的判断,再执行对应的逻辑。比如端口Link 状态变化的Link status change事件,或者PP交给CPU的的报文触发的RX事件。

下面观察一下Marecll CPSS SDK中具体软件流程:

一,中断初始化部分:

交换芯片入口  : cpssInitSystem , 先调用appDemoBoardPhase1Init 进行第一阶段硬件初始化, 

appDemoBoardPhase1Init 调用 boardCfgFuncs->boardGetPpPh1Params 对应的各个具体平台的 getPpPhase1Config ,获取各个具体的硬件参数(中断号):

getPpPhase1Config 使用 extDrvGetPciIntVec ,通过 PRESTERA_IOC_GETINTVEC ioctl 系统调用, 获取到硬件中断号, 保存到localPpPh1Config.intVecNum 中。

appDemoBoardPhase1Init 再调用 sysCfgFuncs->cpssHwPpPhase1Init(appDemoDxHwPpPhase1Init),

appDemoDxHwPpPhase1Init 调用 cpssDxChHwPpPhase1Init , 

cpssDxChHwPpPhase1Init 调用 prvCpssDrvInterruptsInit 初始化中断部分,同时传入中断号,

在 prvCpssDrvInterruptsInit 中,

对芯片中断控制器 prvCpssDrvPpConfig[devNum]->intCtrl 数据结构进行初始化

包括 prvCpssDrvInterruptPpSR 地址赋值给prvCpssDrvPpConfig[devNum]->intCtrl.isrFuncPtr ,

prvCpssDrvInterruptsInit 调用 prvCpssDrvGenExMxDxObjPtr->drvHwPpInterruptInit (drvInterruptsInit)初始化各个端口组(Port Group)的中断,

drvInterruptsInit 调用prvCpssDrvInterruptConnect, 同时将函数 prvCpssDrvInterruptPpSR 地址作为参数,传给prvCpssDrvInterruptConnect, 

prvCpssDrvInterruptConnect   将  prvCpssDrvInterruptPpSR 地址 保存到对应的 prvCpssDrvComIntSvcRtnDb.intVecMap中,

再调用 cpssExtDrvIntConnect(地址为 extDrvIntConnect),将interruptMainSr 地址作为参数传入,

extDrvIntConnect中,  将interruptMainSr 地址赋值给intTask 中的函数指针。 

再调用 PRESTERA_IOC_INTCONNECT ioctl 系统接口,

mvPP.ko 中的prestera_ioctl 对应调用prestera_int_connect 申请软中断和硬中断的中断服务函数。 

调用 tasklet_init 申请软中断服务函数 : prestera_bh

调用request_irq 申请硬号33的中断服务函数 : prestera_tl_isr 

调用完成后, 创建 intTask 线程,通过 PRESTERA_IOC_WAIT 系统调用, 等待中断来临,

mvPP.ko 中, PRESTERA_IOC_WAIT 调用down_interruptible, 根据信号量实现阻塞,等待中断来临。 

第二,中断分发过程:

硬中断33到来,CPU 进入异常向量表, 找到中断地址 irq_handler, irq_handler 宏是 arch_irq_handler_default 的封装,

通过get_irqnr_preamble  保留进程地址,  

通过 get_irqnr_and_base 获取到中断号,将中断号和寄存器列表传给C函数: asm_do_IRQ , 

asm_do_IRQ 调用handle_IRQ,

handle_IRQ 调用中断顶部 generic_handle_irq 处理硬中断, 调用中断号33 对应的中断处理函数prestera_tl_isr  ,调用testklet_schdule(),唤醒HI_SOFTIRQ 类型的软中断 ,

里面调用 中断底部 irq_exit 唤醒软中断服务主函数 invoke_softirq, invoke_softirq 调用do_softirq , 

do_softirq 调用__do_softirq,__do_softirq 根据pending 标记调用对应的软中断服务函数, 这里是 HI_SOFTIRQ 类型的中断服务函数tasklet_hi_action .

 tasklet_hi_action  调用具体软中断服务函数 prestera_bh, 将信号量恢复。 

信号量恢复后intTask 中PRESTERA_IOC_WAIT 的阻塞就会退出, 再调用 interruptMainSr,

interruptMainSr 调用 prvCpssDrvInterruptPpSR,

prvCpssDrvInterruptPpSR调用  prvCpssDrvIntEvReqDrvnScan 遍历各个中断原因寄存器, 调用prvCpssDrvEvReqQInsert 插入具体事件。

prvCpssDrvEvReqQInsert 调用 prvCpssDrvEvReqNotify ,向指定信号量ID发送信号,监听该信号量ID的等待线程结束等待。

cpssOsSigSemSignal , 即osSemSignal ,进行MVKERNELEXT_IOC_SEMSIGNAL 系统调用,

mv_KernelExt.ko 内核模块中, 对应调用mvKernelExt_SemSignal ,发送指定的信号量。 

这样cpssEventSelect 的等待退出,实现了监听指定事件的功能。

第三,事件处理部分:

芯片初始化入口 cpssInitSystem , 结尾阶段调用cpssInitSystem_afterBasicConfig,

cpssInitSystem_afterBasicConfig 调用appDemoEventRequestDrvnModeInit

appDemoEventRequestDrvnModeInit里初始化evHndlrCauseAllArr数组,为10个数组成员绑定不同的事件组,

比如

evHndlrCauseAllArr[9] 里,包含了CPSS_PP_PORT_LINK_STATUS_CHANGED_E      ---------    44

evHndlrCauseAllArr[1]里, 包含CPSS_PP_RX_BUFFER_QUEUE0_E                         ---------    155

evHndlrCauseAllArr[0] 里, 包含 CPSS_PP_PORT_SYNC_STATUS_CHANGED_E    ----------------      52

调用10次 cpssEventBind ,通过事件列表创建了事件处理信息数据结构,将10个事件组成员依次封装位为事件处理信息结构类型, 

cpssEventBind调用

prvCpssEventBind ,调用

cpssOsSigSemBinCreate , 即osSemCCreate 申请信号量ID,

osSemCCreate 通过 CREATE_SEM 宏创建信号量ID 。 

最后,结合事件evHndlrCauseAllArr[]和信号量ID,创建事件处理信息 PRV_CPSS_DRV_EVENT_HNDL_STC 指针,将地址保存到taskParamArr[] 中。

调用osTaskCreate 创建10个线程, 线程地址appDemoEvHndlr ,依次传入taskParamArr[] 各个成员,将事件处理信息交给appDemoEvHndlr 。

appDemoEvHndlr 调用 cpssEventSelect 监听各个事件参数,等待各事件的到来。

cpssEventSelect 通过调用 cpssOsSigSemWait, 即osSemWait, 进行 MVKERNELEXT_IOC_SEMWAIT 系统调用,

mv_KernelExt.ko 内核模块中, 对应调用mvKernelExt_SemWait ,等待指定的信号量,实现事件等待。

 

 当 prvCpssDrvIntEvReqDrvnScan 插入新事件后,调用prvCpssDrvEvReqNotify 唤醒该事件监听的线程, 

进而 appDemoEvHndlr 的cpssEventSelect的监听等待结束。 

cpssEventSelect内部 通过cpssOsSigSemWait 实现等待指定信号量ID, prvCpssDrvEvReqNotify 发送该信号量之后,等待退出,

cpssEventSelect 再调用prvCpssDrvEvReqQBitmapGet 获取到具体事件类型(比如 CPSS_PP_PORT_LINK_STATUS_CHANGED_E)后返回appDemoEvHndlr, 

appDemoEvHndlr  调用cpssEventRecv 获取到具体物理端口号, 再调用 appDemoEnPpEvTreat 处理具体事件.

appDemoEnPpEvTreat 里面, 根据事件类型, 如果是CPSS_PP_RX_BUFFER_QUEUE0_E 类型, 调用cpssEnRxPacketGet 处理收包逻辑。

如果是CPSS_PP_PORT_LINK_STATUS_CHANGED_E 类型, 打印出具体物理端口的 LINK UP/DOWN 的情况

*******************************************

部分代码简介:

appDemoEvHndlr 定义:

appDemoEvHndlr(GT_VOID * param ){

...

GT_U32              evBitmapArr[CPSS_UNI_EV_BITMAP_SIZE_CNS]; 

cpssEventSelect(evHndl, NULL, evBitmapArr, (GT_U32)CPSS_UNI_EV_BITMAP_SIZE_CNS);

CPSS_UNI_EV_BITMAP_SIZE_CNS 定义:

... 

    CPSS_DRAGONITE_ERROR_E                                                    ,  /* 408 */ 

    CPSS_DRAGONITE_UNI_EV_DUMMY_MAX_E,/* dummy -- must be 1 before CPSS_DRAGONITE_UNI_EV_MAX_E */  ---> 409

 

    CPSS_DRAGONITE_UNI_EV_MAX_E                          = (CPSS_DRAGONITE_UNI_EV_DUMMY_MAX_E-1),   -------> 408 

    /************************** End of events *********************************/

    CPSS_UNI_EV_DUMMY_MAX_E,/* dummy -- must be 1 before CPSS_UNI_EVENT_COUNT_E */   ---------> 409 

 

    CPSS_UNI_EVENT_COUNT_E                           = (CPSS_UNI_EV_DUMMY_MAX_E),   --------------> 409

} CPSS_UNI_EV_CAUSE_ENT;

 

#define CPSS_UNI_EV_BITMAP_SIZE_CNS       ((CPSS_UNI_EVENT_COUNT_E + 31) / 32) 

将全部的事件, 作为一个32位的bitmap, 每次只有一个bit 是为1 的,长度 CPSS_UNI_EV_BITMAP_SIZE_CNS = (409 + 31) / 32 = 13

evBitmapArr 是一个13 成员的数组, 里面的每一个bit, 表示一个具体的事件

事件匹配算法:

        for (evCauseIdx = 0; evCauseIdx < CPSS_UNI_EV_BITMAP_SIZE_CNS; evCauseIdx++)

        {                         

            if (evBitmapArr[evCauseIdx] == 0)   --------------------> 先找到当前非0 的成员

            {                     

                continue;         

            }                                                       

            evBitmap = evBitmapArr[evCauseIdx];                                 

            for (i = 0; evBitmap; evBitmap >>= 1, i++) --------------> 再在当前成员里面, 找到具体的bit

            {                     

                if ((evBitmap & 1) == 0)                   ------------------------> 找到非0 的bit,i 表示个数,i 表示在当前的数组里面的偏移

                {                 

                    continue; 

                }                                                   

                uniEv = (evCauseIdx << 5) + i;   --> 事件类型就是evCauseIdx 索引 * 32  + 偏移,这个就是在整个事件类型枚举里面的数值

if (cpssEventRecv(evHndl, uniEv, &evExtData, &devNum) == GT_OK){  ------------> 获取到具体的物理端口号

....

appDemoEnPpEvTreat(evHndl, uniEv, &evExtData, &devNum)  -------------> 传入端口号,处理具体事件

}

**********************************************

1,prvCpssDrvIntEvReqDrvnScan , 插入的就是具体事件类型原理

2, prvCpssDrvEvReqQInsert,  插入事件原理  -->将事件插入事件控制队列尾部,向事件处理信息中的信号量ID发送信号,实现监听线程等待结束

typedef struct PRV_CPSS_DRV_EV_REQ_Q_CTRL_STCT

{

    PRV_CPSS_DRV_EV_REQ_NODE_STC  *evNodeList;           -------------->  事件头节点信息                                                                                            

    GT_U32                          uniEvCause; ---------------->  事件原因

    PRV_CPSS_DRV_EVENT_HNDL_STC    *userHndlPtr;       ---------------->  事件处理信息

    struct PRV_CPSS_DRV_EV_REQ_Q_CTRL_STCT  *nextPtr;    ------------->  事件控制队列中下一节点

} PRV_CPSS_DRV_EV_REQ_Q_CTRL_STC; ---------------->  事件控制队列

typedef struct PRV_CPSS_DRV_EV_REQ_NODE_STCT --------------> 事件节点数据结构

{                    

    GT_U8                           devNum; -------------->  设备ID

    GT_U32                          portGroupId;             -------------->  端口组ID

    GT_U8                           intStatus;                  -------------->  中断状态

    GT_U8                           intRecStatus;                          --------------> 中断接收时间

    GT_U32                          intCause;                -------------->  中断原因索引值

    GT_U32                          uniEvCause;              -------------->  事件原因索引值

    GT_U32                          uniEvExt;                -------------->  事件附带信息(如物理端口ID)

    PRV_CPSS_DRV_REQ_DRVN_MASK_SET_FUNC  intMaskSetFptr;     --------------->  事件掩码方法

    PRV_CPSS_DRV_EV_DRVN_INT_REC_CB intCbFuncPtr;              ----------------->  回调函数, 向指定任务发送信号

    GT_U32                          intMaskReg;                    ----------> 掩码后的中断寄存器

    GT_U32                          intBitMask;                    ---------->  掩码后的中断寄存器的bit

    struct PRV_CPSS_DRV_EV_REQ_NODE_STCT *prevPtr;        ---------> 事件链表中前一节点  

    struct PRV_CPSS_DRV_EV_REQ_NODE_STCT *nextPtr;           --------> 事件链表中后一节点

}PRV_CPSS_DRV_EV_REQ_NODE_STC;

事件插入函数: prvCpssDrvEvReqQInsert  : 向事件控制队列尾部插入新事件

GT_STATUS prvCpssDrvEvReqQInsert

(                            

    IN PRV_CPSS_DRV_EV_REQ_NODE_STC      evNodePool[],            ----> 中断节点池地址

    IN GT_U32           intIndex,               ----> 中断池中的索引值

    IN GT_BOOL          masked                       ----> 是否需要掩码

)                            

{                            

    PRV_CPSS_DRV_EV_REQ_NODE_STC     *newNode;     -------> 要插入的新事件节点

    PRV_CPSS_DRV_EV_REQ_Q_CTRL_STC   *evQueue;     ------->  事件控制队列

                             

    newNode = &(evNodePool[intIndex]); ------> 中断节点池中的节点地址,就是要插入的新事件节点

                     

    switch (newNode->intRecStatus) -------> 判断节点的中断状态(正常进入至此的状态是: PRV_CPSS_DRV_EV_DRVN_INT_IDLE_E)

    {                        

        case PRV_CPSS_DRV_EV_DRVN_INT_RCVD_E: -------> 中断在系统正常操作下接受

            newNode->intRecStatus = PRV_CPSS_DRV_EV_DRVN_INT_MASKED_E;   -------> 标记为中断被掩盖,函数退出

            return GT_OK;    

                             

        case PRV_CPSS_DRV_EV_DRVN_INT_MASKED_E:         ------>  中断是被掩盖的,直接退出

            return GT_OK;    

                             

        default:             

            break;           

    }                        

       if (GT_TRUE == masked)  {        -----------> 如果标记为掩码,退出                 

        if(newNode->intRecStatus == PRV_CPSS_DRV_EV_DRVN_INT_IDLE_READY_E)  {  

            newNode->intRecStatus = PRV_CPSS_DRV_EV_DRVN_INT_MASKED_E;

        }                     

        return GT_OK;         

    }                                                       

    evQueue = &(prvCpssDrvComIntEvReqQueuesDb.uniEvQArr[newNode->uniEvCause]);   ------------> 在事件数据库里获取到事件的控制队列                          

    if(newNode->intRecStatus != PRV_CPSS_DRV_EV_DRVN_INT_IDLE_READY_E)

    {                         

        newNode->intRecStatus = PRV_CPSS_DRV_EV_DRVN_INT_RCVD_E;

                              

        if (NULL == evQueue->evNodeList) {                                 ----------------> 头节点为空, 将新节点作为头节点 

            evQueue->evNodeList = newNode;

            newNode->nextPtr    = newNode;

            newNode->prevPtr    = newNode;

        }  else  {

            evQueue->evNodeList->prevPtr->nextPtr   = newNode;    ---->  将新节点添加到事件链表尾部,由于双向链表,头节点上一个节点就是原尾节点

            newNode->nextPtr                        = evQueue->evNodeList;    ----> 新节点的下一节点指向头节点

            newNode->prevPtr                        = evQueue->evNodeList->prevPtr;   ---->  新节点前节点指向原头节点的上节点(原尾节点)

            evQueue->evNodeList->prevPtr            = newNode;    ------>  头节点的新前节点指向新节点, 完成新节点的插入

        }                     

    }                         

    else    {  

        newNode->intRecStatus = PRV_CPSS_DRV_EV_DRVN_INT_MASKED_E;

    }                        

                             

    if (NULL != evQueue->userHndlPtr){ 

        prvCpssDrvEvReqNotify(evQueue->userHndlPtr);   -----------> 将事件的处理信息传给事件通知函数

    }                        

                             

    return GT_OK;            

}                            

                           

GT_VOID prvCpssDrvEvReqNotify

(                     

    IN PRV_CPSS_DRV_EVENT_HNDL_STC      *hndlEvPtr

)                     

{         

...

            cpssOsSigSemSignal((CPSS_OS_SIG_SEM) PRV_CPSS_DRV_HANDEL_SEM_MAC(hndlEvPtr));  ----> cpssOsSigSemSignal :osSemSignal

----------->  PRV_CPSS_DRV_HANDEL_SEM_MAC: (handlerPtr)->hndlBindInfo.semInfo.semId

-----------> 发送事件处理信息中的信号量ID,等待该信号量的线程实现等待结束

...         

}  

***********************

cpssEventSelect : 

调用cpssOsSigSemWait 等待指定信号量,

等待完成后, 调用prvCpssDrvEvReqQBitmapGet 获取到事件具体,并转化位bitmap, 返回获取到的事件个数。

GT_U32 prvCpssDrvEvReqQBitmapGet                             

(                                                            

    IN GT_UINTPTR       hndl,                                  ------->    PRV_CPSS_DRV_EVENT_HNDL_STC 事件处理信息数据结构的地址

    INOUT GT_U32        evBitmapArr[],                    ------->     bitmap          

    IN GT_U32           evBitmapLength                     ---------> bitmap 长度

)                                                            

{                                                            

    GT_U32              evCount;                                         ------> 新事件计数

    PRV_CPSS_DRV_EVENT_HNDL_STC *hndlEvPtr;           -------> 事件处理信息指针

    PRV_CPSS_DRV_EV_REQ_Q_CTRL_STC       *evReqPtr;       ---------> 事件控制队列

                                                             

    evCount             = 0;                                 

    hndlEvPtr           = (PRV_CPSS_DRV_EVENT_HNDL_STC*)hndl;   -------> 获取到事件处理信息结构

    evReqPtr            = hndlEvPtr->evListPtr;                     

                                                             

    if (NULL != evBitmapArr)                                 

    {                                                                                  

        cpssOsBzero((GT_VOID*)evBitmapArr, evBitmapLength * sizeof(evBitmapArr[0]));    -------> 将bmp 清零

    }                                                        

                                                             

    if (PRV_CPSS_DRV_TX_BUFF_QUEUE_EVENT_E == hndlEvPtr->evType)   ------> 事件处理类型为2类,一类是“TX buffer ”事件, 另一类是普通事件

    {                                                        

        if (NULL != ((PRV_CPSS_TX_BUF_QUEUE_FIFO_STC *)hndlEvPtr->extDataPtr)->headPtr)

        {                                                    

            /* at least one TxBufferQueue in FIFO */         

            if (NULL != evBitmapArr)                         

            {                                                

                evBitmapArr[evReqPtr->uniEvCause >> 5] |=    

                                            (1 << (evReqPtr->uniEvCause & 0x1F));

            }                                                

            evCount = 1;                                     

        }                                                    

    }                                                        

    else                                                     

    {                                                        

        if (NULL == evBitmapArr)                          ---->     evBitmapArr指针为空, 仅统计事件个数

        {                                                                 

            while (evReqPtr)                                  

            {                                                 

                if (evReqPtr->evNodeList != NULL)              ----> 统计事件处理信息中,事件控制队列的长度表示有多少事件等待被处理

                {                                             

                    evCount++;                                

                }                                                    

                evReqPtr = evReqPtr->nextPtr;                 

            }                                                 

        }                                                     

        else                                                  

        {                                                     

            while (evReqPtr)                                  

            {                                                 

                if (evReqPtr->evNodeList != NULL &&      ----> 如果事件控制列表头节点不为空,  事件原因在事件类型枚举里

                    (evReqPtr->uniEvCause >> 5) < evBitmapLength)

                {                                                                   -----------> 这说明当接收到这个事件处理信息后,事件原因就是确定的

                    evBitmapArr[evReqPtr->uniEvCause >> 5] |=       ---->  prvCpssDrvEvReqQBitmapGet作用就是将事件原因转化到bitmap中

                                            (1 << (evReqPtr->uniEvCause & 0x1F));    ------------> evReqPtr->uniEvCause >> 5 作用计算事件类型所在的 bit组,

-------->  (1 << (evReqPtr->uniEvCause & 0x1F) 作用获取到具体的bit,存入bit[] 中

                    evCount++;                                

                }                                             

                /* move to the next binded event */           

                evReqPtr = evReqPtr->nextPtr;                 

            }                                                 

        }                                                     

    }                                                         

                                                              

    return evCount;                                           

GT_U8 prvCpssDrvIntEvReqDrvnScan        

(                                       

    GT_U8               devNum,         

    GT_U32              portGroupId,    

    PRV_CPSS_DRV_EV_REQ_NODE_STC            *intNodesPool,

    GT_U32              *intMaskShadow, 

    PRV_CPSS_DRV_INTERRUPT_SCAN_STC      *pIntScanNode

)                                       

{                                       

    PRV_CPSS_DRV_EV_REQ_NODE_STC    *evNodesPool;      ------------> 事件节点

    PRV_CPSS_DRV_INTERRUPT_SCAN_STC *intScanStackPtr[PRV_CPSS_DRV_SCAN_STACK_SIZE_CNS];   ---> 中断寄存器查询树指针

    PRV_CPSS_DRV_INTERRUPT_SCAN_STC *currIntScanPtr;      ----> 当前树节点

    PRV_CPSS_DRV_INTERRUPT_SCAN_STC **intStackTopPtr;   ----> 树顶

    GT_U32              i;         

    GT_U32              tmpCauseBits; 

    GT_U32              intMaskBits;  

    GT_U32              intMaskBit;    

    GT_U32              *maskShdwValPtr; 

    GT_BOOL             intMasked;   

    GT_U32              intCause;   

    GT_U32              newInt;  

                                        

    evNodesPool = intNodesPool;         

    newInt = 0;                         

                                        

    /* push */                          

    intScanStackPtr[0] = pIntScanNode;  

    intStackTopPtr = &intScanStackPtr[0];

    while (intStackTopPtr >= &intScanStackPtr[0]) {                        

        currIntScanPtr = *intStackTopPtr;

        intStackTopPtr--;    

                             

...               

        --------> currIntScanPtr->pRegReadFunc 的地址是: prvCpssDrvHwPpPortGroupIsrRead

          ------------->currIntScanPtr->causeRegAddr 地址是 0x30,将图1 (  Global Interrupts Summary Cause Register )

        currIntScanPtr->pRegReadFunc(devNum, portGroupId,currIntScanPtr->causeRegAddr,

                                     &intCause);

                      

        maskShdwValPtr = &intMaskShadow[currIntScanPtr->startIdx >> 5];    --------> 转换到对应的bitmap掩码

                             

        tmpCauseBits = intCause & currIntScanPtr->nonSumBitMask;       ---------->经过掩码后的数值 

        intMaskBits = 0;     

                             

        if (tmpCauseBits != 0)   ------------> 经过掩码后的数值不为0,则获取到具体的事件

        {              

            tmpCauseBits >>= currIntScanPtr->startIdx & 0x1f;    -------> 转化为 0-31 bit 之间

       

获取到事件类型算法:

                                  

            for (i = currIntScanPtr->startIdx;

                 (i <= currIntScanPtr->endIdx && tmpCauseBits);    ----------------> 循环遍历

                 i++, tmpCauseBits >>= 1)

            {                            

                if (tmpCauseBits & 1)    

                { 

...               

--------------> evNodesPool[i].uniEvCause 就是具体的事件枚举

                   if (NULL != prvCpssDrvEvReqQUserHndlGet(evNodesPool[i].uniEvCause))   ----> 获取到事件处理信息

                    {                    

                        /* insert the event into queue */

                        intMaskBit = 1 << (i & 0x1f);

                        intMaskBits |= intMaskBit;

                                         

                        if (*maskShdwValPtr & intMaskBit)

                        {                

                            intMasked = GT_FALSE;

                            newInt = 1;  

                        }                

                        else             

                        {                

                            intMasked = GT_TRUE;

                        }                                   

                        prvCpssDrvEvReqQInsert(evNodesPool, i, intMasked);   ---------> 插入新事件,当前事件处理信息中附带具体事件枚举

                    }                    

                    else                 

                    {                    

                        newInt = 1;      

                    }                    

                }                        

            }                            

            *maskShdwValPtr &= ~(intMaskBits & currIntScanPtr->maskRcvIntrEn);

            currIntScanPtr->pRegWriteFunc(devNum,portGroupId,

                                          currIntScanPtr->maskRegAddr,

                                          *maskShdwValPtr);   

        }                                

                                         

        /* Scan summary interrupt bits.                 */

        tmpCauseBits = (intCause & ~(currIntScanPtr->nonSumBitMask) &

                        *maskShdwValPtr);

                                         

        if(tmpCauseBits != 0)            

        {                                

            for(i = 0; i < currIntScanPtr->subIntrListLen; i++)

            {                            

                if (((tmpCauseBits >> ((currIntScanPtr->subIntrScan[i]->bitNum)

                                       & 0x1f)) & 1) == 1)

                {                        

                    intStackTopPtr++;    

                    if (intStackTopPtr > &intScanStackPtr[PRV_CPSS_DRV_SCAN_STACK_LAST_ENTRY_CNS])

                    {                    

                        DBG_LOG(("scan stack overflow !!\n",1,2,3,4,5,6));

                        return 0;        

                    }                    

                    *intStackTopPtr = currIntScanPtr->subIntrScan[i];

                }                        

            }                            

        }                                

                                         

        if(currIntScanPtr->rwBitMask != 0)

        {                                

            currIntScanPtr->pRegWriteFunc(devNum,portGroupId,currIntScanPtr->causeRegAddr,

                                        ~(intCause & currIntScanPtr->rwBitMask));

        }                                

    }           &nb




################################################################
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
xcat-mediastream是一款用于下载和管理媒体资源的工具。它可以让用户方便地从互联网上获取音频、视频、图片等文件,并且支持管理和整理这些文件。 使用xcat-mediastream进行下载非常简单。首先,用户需要将xcat-mediastream安装到自己的设备上,这可以通过在官方网站上下载安装包或者通过软件包管理器进行安装来完成。安装完成后,用户可以打开xcat-mediastream应用程序。 在xcat-mediastream的界面中,用户可以看到一个搜索栏和下载区域。用户可以在搜索栏中输入关键词来搜索他们感兴趣的媒体资源,如电影、音乐专辑或者图片集。之后,xcat-mediastream会通过互联网去搜索匹配的资源。 搜索完成后,用户可以在搜索结果中挑选自己想要下载的文件。xcat-mediastream会提供关于文件大小、格式、下载速度等信息来帮助用户做出选择。选中文件后,用户可以点击下载按钮将文件保存到本地设备中。 除了下载功能,xcat-mediastream还支持对已下载的媒体资源进行管理和整理。用户可以在应用程序中创建不同的媒体文件夹,并将下载的文件分类保存到相应的文件夹中。这样可以让用户更好地管理自己的媒体资源,并方便地查找和播放它们。 总的来说,xcat-mediastream是一个功能强大、易于使用的媒体下载和管理工具,可以帮助用户方便地从互联网上获取媒体资源,并有效地管理这些资源。无论是下载电影、音乐还是保存图片,xcat-mediastream都能满足用户的需求,并提供良好的使用体验。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值