中断处理

首先在获得PCI配置空间资源的时候,就要获得中断资源,根据CM_PARTIAL_RESOURCE_DESCRIPTOR 结构的 Type 域来区分需要获得什么样的中断资源的时候,如果Type类型为:CmResourceTypeInterrupt,此时需要将中断资源从CM_PARTIAL_RESOURCE_DESCRIPTOR中取出:

irql = (KIRQL) resource->u.Interrupt.Level;              //中断级别

                            vector = resource->u.Interrupt.Vector;                     // 中断向量

                            affinity = resource->u.Interrupt.Affinity;

                            mode = (resource->Flags == CM_RESOURCE_INTERRUPT_LATCHED)

                                     ? Latched : LevelSensitive;

                            irqshare = resource->ShareDisposition == CmResourceShareShared;

                            gotinterrupt = TRUE;

获取以上这些信心之后我们就可以注册中断,通过IoConnectInterrupt()函数来实现:

函数定义如下:

IoConnectInterrupt

OUT PKINTERRUPT *InterruptObject,//指向驱动程序提供的中断对象存储地址,该参数随后

//要传递给KeSynchronizeExecution。

OUT PKSERVICE_ROUTINE ServiceRoutine,//中断服务例程的入口

IN PVOID ServiceContext,//指向驱动指定的即将传递给ISR的参数,ServiceContext必须在

//常驻内存中,可以是驱动程序创建的设备驱动的设备扩展,也可

//以是驱动创建的控制对象的控制拓展,还可以是设备驱动分配的

//非分页内存。

IN PKSPIN_LOCK SpinLock OPTIONAL,//指向已经初始化的自旋所,驱动程序负责自旋所的存

//储,并且该自旋所将用来同步被驱动程序其它例程共

//享的数据的访问,该参数在ISR处理多个中断向量或

//者驱动程序包含不止一个ISR时需要设置,否则,驱

//动程序不需要为中断自旋所分配存储空间,参数设置

//为NULL。

IN ULONG Vector,                //输入获取的中断向量

IN KIRQL Irql,                    //输入获取的中断优先级DIRQL

IN KIRQL SynchronizeIrql,         //指明ISR执行所在的DIRQL,当ISR需要处理多个中断

//向量或者驱动程序有多个ISR的时候,该值选择全部中

//断资源的u.Interrupt.Level中的最高值,否则和上面的

//Irql变量相等。

IN KINTERRUPT_MODE InterruptMode,//电平触发或者边沿触发

IN BOOLEAN ShareVector,           //指明中断向量是否是可共享的。

IN KAFFINITY ProcessorEnableMask,//指定一个KAFFINITY值,用来说明设备中断可以在什

//么样的处理器平台上发生。

IN BOOLEAN FloatingSave          //指明是否需要保存设备中断时的浮点堆栈,在X86平

//台下,该值必须是FALSE。

);

实际使用时:

status = IoConnectInterrupt(&pdx->InterruptObject, (PKSERVICE_ROUTINE) ISRInterrupt,

                   (PVOID) pdx, NULL, vector, irql, irql, mode, irqshare, affinity, FALSE);

第二个参数为我们自定义的中断服务例程,当驱动通过这个函数接收中断,之后调用相应的DPC(deferred procedure calls,延迟过程调用)处理函数,DPC的使用主要是为了提高处理效率。但是首先要注册DPC处理函数,通过:

VOID KeInitializeDpc(

  __out     PRKDPC Dpc,

  __in      PKDEFERRED_ROUTINE DeferredRoutine,

  __in_opt  PVOID DeferredContext

);

来实现注册DPC处理函数。

实际应用:

KeInitializeDpc(&pdx->fdo->Dpc,DPCForISR,NULL);

BOOLEAN ISRInterrupt(PKINTERRUPT InterruptObject, PDEVICE_EXTENSION pdx)

{                                                               

         //中断响应,通知硬件该中断已经处理,不用再发该中断

         WRITE_REGISTER_ULONG((PULONG) &pdx->pHBARegs->IntrMask,0x00000001);

         pdx->inthw_cnt++;

         KeInsertQueueDpc(&pdx->fdo->Dpc,pdx->fdo,pdx->fdo->CurrentIrp );

         return TRUE;

}

在该函数中,将中断请求插入到中断处理队列中,交由DPC来处理

BOOLEAN KeInsertQueueDpc(

  __inout   PRKDPC Dpc,

  __in_opt  PVOID SystemArgument1,

  __in_opt  PVOID SystemArgument2

);

DPC的标准格式为:

KDEFERRED_ROUTINE CustomDpc;

VOID CustomDpc(

  __in      struct _KDPC *Dpc,

  __in_opt  PVOID DeferredContext,

  __in_opt  PVOID SystemArgument1,

  __in_opt  PVOID SystemArgument2

)

{ ... }

其中参数(注意来源):

Dpc [in]

Caller-supplied pointer to a KDPC structure, which represents the DPC object that is associated with this CustomDpc routine.

DeferredContext [in, optional]

Caller-supplied pointer to driver-defined context information that was specified in a previous call to KeInitializeDpc.

SystemArgument1 [in, optional]

Caller-supplied pointer to driver-supplied information that was specified in a previous call to KeInsertQueueDpc.

SystemArgument2 [in, optional]

Caller-supplied pointer to driver-supplied information that was specified in a previous call to KeInsertQueueDpc.

这里DPC可以这样设计:

VOID DPCForISR(IN PKDPC Dpc,IN PVOID Context,IN  PVOID fdo,IN PVOID pIrp)

{

         PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)

((PDEVICE_OBJECT)fdo)->DeviceExtension;

        

         KeAcquireSpinLock(&pdx->spinLock,&pdx->oldIrql);

//Int_stat 寄存器由硬件填写的

         int_status = READ_REGISTER_ULONG((PULONG) &pdx->pHBARegs->Int_stat);

         KdPrint(("interrupt!int status is 0x%0x\n",int_status));

         while(int_status) //循环处理中断,直到处理完

         {

                  

                   if(int_status >= INT_RECV_0 && int_status <= INT_RECV_23)

                   {

                            RecvTask(pdx,int_status-1);

                            pdx->recint_cnt[int_status-1]++;

                   }

                   if(int_status >= INT_LINK0_BUILD && int_status <= INT_LINK3_BUILD)

                   {

                            pdx->rx_fc_desc_buf_virt[int_status-25]->link_state=1;

                            pdx->bldint_cnt[int_status-25]++;

                            KdPrint(("Build!int status is 0x%0x\n",int_status));

                   }

                  

                   if(int_status >= INT_LINK0_BREAK && int_status <= INT_LINK3_BREAK)

                   {

                            pdx->rx_fc_desc_buf_virt[int_status-29]->link_state=0;

                            pdx->brkint_cnt[int_status-29]++;

                   }

                   pdx->intsw_cnt++;

                   int_status = READ_REGISTER_ULONG((PULONG) &pdx->pHBARegs->Int_stat);

                   KdPrint(("interrupt!int status is 0x%0x\n",int_status));

         }

        

         return ;             

}

在DPC中完成具体的操作,这样中断处理就结束了。

make it simple, make it happen
原文地址:https://www.cnblogs.com/zhuyp1015/p/2396630.html