Jungo WinDriver: テクニカル ドキュメント #125

WinDriver
テクニカル ドキュメント #125


Doc ID: 125
製品: WinDriver
Version:  All

Kernel PlugIn を使用した共有 PCI 割り込みの処理

PCI の割り込みは、通常、カーネル レベルで処理されます。このため、WinDriver を使用して PCI の割り込みを処理する最善の方法は、Kernel PlugIn 機能を使用することです。Kernel PlugIn 機能で、カーネル レベルで直接コードを実装できます。特に、異なるハードウェア間で共有される割り込みを処理する際に最適です (これらの割り込みは定義によって、共有できるので)。PCI 割り込みの場合には、非常に一般的です。WinDriver を使用した割り込みの共有可能な状態の設定方法に関しては、テクニカル ドキュメント #21 を参照してください。

Kernel PlugIn には、以下の二つの割り込み処理関数があります:

  1. KP_IntAtIrql: この関数は、割り込みの検出時に、即座に高い IRQ (Interrupt Request) のレベルで、カーネルで実行されます。この関数には、割り込みのスースをクリアし (割り込みの検出)、さらに優先度の高い割り込み処理のコード用の write / read コマンドおよびが含まれます。更に遅延カーネルモードまたはユーザーモードの割り込み処理を必要とする場合、KP_IntAtIrql は TRUE を返します。この場合、高レベルな処理を終了すると、遅延 KP_IntAtDpc() ルーティンを実行します。その他の場合、この関数は FALSE を返し、KP_IntAtDpc() (その他のユーザーモードの割り込み処理ルーティンと同様) を実行しません。たとえば、WinDriver のサンプルまたは DriverWizard で生成された Kernel PlugIn プロジェクトは、KP_IntAtIrql() の 5 回の呼出しに対して 1回のみ TRUE を返すようにコードを実装することによって、5 回の割り込みに対して、1 回、遅延の割り込み処理をスケジュールします。


  2. KP_IntAtDpc: この関数は、KP_IntAtIrql() が TRUE を返し、かつ、実行する優先度の低いカーネルの割り込み処理を含む場合に、高い実行レベルで、カーネルで実行されます。
    この関数の戻り値は、コントロールがユーザーモードへ返すと、ユーザーモードの割り込み処理ルーティン (ユーザーモードから InterruptEnable() への呼び出しで設定できます) を実行する回数で決まります。

WinDriver を使用して、共有 PCI 割り込みを処理するには、以下のステップを実行します:
  1. WinDriver の DriverWizard ユーティリティを使用して、Kernel PlugIn のプロジェクトを作成します (Wizard で C のコードを生成する際に、Kernel PlugIn のコードを生成するオプションがあります - チェックボックスをチェックし、コードの生成を行います)。または、汎用的な WinDriver の Kernel PlugIn のサンプル - KPTEST - (Kernel PlugIn プロジェクトの基本) を使用することもできます。

    Wizard を使用する利点は、生成されたコードは、対象のデバイス用に検出された特定のデバイスの構成情報を使用します。同様に、Wizard で定義したハードウェア独自の情報も使用します。PCI 割り込みを処理するために Kernel PlugIn のコードを生成する場合、Wizard の Register タブで、割り込みの検出するためにアクセスするレジスタを定義し、Interrupts タブから割り込みに対して、高い IRQ のレベル (KP_IntAtIrql()) で実行するレジスタの read / write コマンドを割り当てます。割り込みの検出のための実際のコマンドは、ハードウェアの仕様によるので、正しいコマンドを設定するには、ハードウェアの仕様に従ってください。
    レジスタと割り込みコマンドを定義する方法に関する詳細は、テクニカル ドキュメント #105 を参照してください。

  2. Kernel PlugIn を使用して、PCI 割り込みを処理する正しい方法は (特に共有割り込み)、ハードウェアが割り込みを生成したかを判断するには、ハードウェアから情報を読み込む KP_IntAtIrql() にコマンドを含めることです (通常、割り込みのステータス レジスタから読み込みます - INTCSR)。[Kernel PlugIn のコードを生成する前に、Driver Wizard で関連する read コマンドを定義するか、または生成されたコードまたはサンプルのコードを編集して、コマンドを追加します。] 割り込みがハードウェアによって生成された場合、その関数は、割り込みのコントロールを受信するために、fIsMyInterrupt パラメータの値に TRUE を設定し、割り込みを検出するために、関連するハードウェアのレジスタを read / write するように処理し、追加の遅延処理を実行するには、TRUE を返し、そのような処理が必要ない場合には、FALSE を返します。ただし、割り込みがハードウェアによって生成されていないと判断する場合、その割り込みをシステムの他のドライバに渡すには、fIsMyInterrupt には FALSE を設定する必要があります。そして、KP_IntAtIrql() は FALSE を返します。(割り込みがそのドライバに属してる場合でも、fIsMyInterrupt に FALSE を設定することに章害はありません。DriverWizard で生成されるコードにはデフォルトでそのように設定されています。システムの他のドライバ (正しく実装されていると仮定して) は、割り込みがハードウェアによって生成されていない場合には、その割り込みを処理しないはずです。)

    ハードウェアが生成した割り込みであるかどうかを確認する一部のコードは、Wizard では定義できない、ハードウェア独自のロジックをベースとしています。このため、Wizard で生成されたコードまたはサンプルのコードの KP_IntAtIrql() の実装を修正し、関連する "if" 文を追加して、ハードウェアによって生成されていない割り込みのコントロールを受信しない、およびハードウェアでそのような割り込みのソースをクリアしないようにします。

    以下は、サンプルの KP_IntAtIrql() の実装です。WinDriver v6.23 の Driver Wizard を使用して生成された Kernel PlugIn のコードです。このコードは、INTCSR メモリ レジスタを read し (コード中に定義)、INTCSR から read した値が 0xFF の場合、割り込みのコントロールの受信および割り込みの検知のみを処理ます (このサンプルでは、ハードウェアが割り込みを生成したことを表しています)。このサンプルでは、INTCSR から読み込まれた INTCSR の値へ書き戻すことによって、割り込みを検出します:

    
    BOOL __cdecl KP_XXX_IntAtIrql(PVOID pIntContext, BOOL *pfIsMyInterrupt)
    {
        XXX_HANDLE hXXX = (XXX_HANDLE) pIntContext;
        DWORD data = 0;
        PVOID pData = NULL;
        DWORD addrSpace;
        WD_ITEMS *pItem;
    
        addrSpace = XXX_INTCSR_SPACE;
        pItem = &hXXX->cardReg.Card.Item[hXXX->addrDesc[addrSpace].index];
        pData = (DWORD*)pItem->I.Mem.dwTransAddr;
        (DWORD)pData += XXX_INTCSR_OFFSET; 
        data = dtoh32(*((DWORD*)pData));
    
        if (data == 0xFF)  // The interrupt was generated by our hardware
        {
            // Write 0x0 to INTCSR to acknowledge the interrupt
            *((DWORD*)pData) = dtoh32(data);
            
            // Accept control of the interrupt
            *pfIsMyInterrupt = TRUE; 
            
            // Schedule deferred interrupt processing (KP_IntAtDpc())
            return TRUE;
         }
        else
        {	
            // (Do not acknowledge the interrupt)
    
            // Do not accept control of the interrupt		
            *pfIsMyInterrupt = FALSE;
            
            // Do not schedule deferred interrupt processing	
            return FALSE;
        }
    }