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



Doc ID: 41
製品: WinDriver
Version:  --

Kernel PlugIn と DMA またはその他の目的のユーザー モード プロジェクト間で、メモリ バッファをどのように共有しますか?

この方法で共有されるメモリをページ ロックする必要があります (例、メモリをハード ディスクへ交換できないし、また CPU にキャッシュできません)。その結果、すべてのプロセッサからメモリに安全にアクセスできます。

カーネルでバッファを割り当てるには、先ず、ユーザー モードから WD_DmaLock () 関数を呼ぶ必要があります。連続するカーネル バッファを割り当てるには、WD_DMA 構造体のdwOptions フィールドに DMA_KERNEL_BUFFER_ALLOC フラグを設定します。WD_DmaLock() 関数は、メモリのユーザー モード マップ (dma.pUserAddr) と同様に、割り当てられたメモリの物理アドレス (dma.Page[0].pPhysicalAddr) を返します。[注意: Solaris では、ユーザー マップを返しません]。WinDriver v6.00 以降、連続する Buffer DMA 割り当ての場合、WD_DMALock() 関数は、dma.pKernelAddr 内で、割り当てられたバッファのカーネル バッファを返します。

malloc() コールの結果として、戻されたユーザー モード マップ アドレス (dma.pUserAddr) を使用して、ユーザー モード アプリケーションから割り当てられたメモリにアクセスできます。Solaris (v6.10 以降) の場合、WD_Transfer() 関数を使用して、メモリにアクセスし、WD_DMALock() 関数から戻されたアドレス (dma.pKernelAddr) のカーネル マップに渡します。

Kernel PlugIn アプリケーションから共有メモリ バッファへアクセスするには、Kernel PlugIn へ割り当てられたバッファのカーネル マップを渡す必要があります。上記で説明したとおり、WinDriver v6.00 以降を使用してる場合、このアドレスを WD_DMALock() 関数で dma.pKernelAddr に返します。[WinDriver v5.22 またはそれ以前を使用してる場合、WD_CardRegister() 関数で、cardReg.Card.Item[0].dwTransAddr に返される、物理アドレスのカーネル バッファを受信するために、物理ベース アドレスと割り当てられたバッファ長を渡し (それぞれ、cardReg.Card.Item[0].I.Mem. dwPhysicalAddr および cardReg.Card.Item[0].I.Mem. dwBytes に設定)、WD_DMALock() 関数からの戻りとして (それぞれ、dma.Page[0].pPhysicalAddr および dma.Page[0].dwBytes に設定)、まず、WD_CardRegister() 関数を呼びます。そして、以下の dma.pKernelAddr の説明のように、このカーネル マップ アドレスを Kernel PlugIn へ渡すことができます]。

[ユーザー モードで直接カーネル マップ アドレスにアクセスしないで下さい。これはカーネル モード アドレスなので、ユーザー モードからアクセスすると、例外保護違反となります。ユーザー モードから直接アクセスするには、ユーザー モード マップ dma.pUserAddr を使用するか、カーネル マップ アドレスで WD_Transfer() 関数を呼びます]。

カーネル仮想アドレスを Kernel PlugIn へ渡します。
このためには、WD_KERNEL_PLUGIN_CALL 構造体を作成し、pData フィールドを設定して、仮想カーネル アドレス dma.pKernelAddr を保持します (以前のバージョンでは、cardReg.Card.Item[0].dwTransAddr)。

そして、以下の方法の何れかで、この構造体を Kernel PlugIn へ渡すことができます:
  1. WD_KernelPlugInCall() への呼び出し。この場合、Kernel PlugIn の KP_Call() コールバック関数内からカーネル仮想アドレスにアクセスできます。
  2. WD_IntEnable()/InterruptEnable() 関数 (または、v5.22 以前の場合、Interrupt ThreadEnable() 関数) への呼び出し。この場合、ユーザー モードで、関数へ渡される WD_INTERRUPT 構造体の kpCall フィールドの WD_KERNEL_PLUGIN_CALL 構造体を保存します。そして、Kernel PlugIn で KP_IntEnable() コールバック関数内からカーネル仮想アドレスにアクセスできます。
Kernel PlugIn でカーネル仮想バッファ アドレスを取得後、Kernel PlugIn モジュールに保存してください (グローバル変数または割り当てられたメモリの場所)。これによって、カーネル モードおよびユーザー モードの両方で同じメモリ バッファにアクセスできます。
 
  *********************************************************************

  // sample user mode code:

  HANDLE hWD;
  WD_DMA dma;
  WD_KERNEL_PLUGIN kerPlug;
  WD_KERNEL_PLUGIN_CALL kpCall;
  WD_CARD_REGISTER cardReg;  // For version 5.22 or below
  
  hWD = WD_Open();

  BZERO(dma);
  dma.pUserAddr = NULL;
  dma.dwBytes = 0x10000;  // allocate 16K
  dma.dwOptions = DMA_KERNEL_BUFFER_ALLOC; // kernel contiguous buffer
  WD_DMALock(hWD, &dma);
  if (!dma.hDma)
  {
     printf("failed allocating dma buffer\n");
     // exit
  }
  
  // At this point, dma.pUserAddr holds the 
  // user mode mapping of the allocated memory, 
  // dma.pKernelAddr holds the kernel mapping of  
  // the memory, and dma.Page[0].pPhysicalAddr 
  // holds the physical memory address.
  
  ////////////////////////////////////////////////////////////////

  // The following is required only for version 5.22 or 
  // earlier of WinDriver:
 
  BZERO(cardReg);
  cardReg.Card.dwItems = 1;
  cardReg.Card.Item[0].item = ITEM_MEMORY;
  cardReg.Card.Item[0].I.Mem.dwBytes = dma.dwBytes;
  cardReg.Card.Item[0].I.Mem.dwPhysicalAddr = 
      (DWORD)dma.Page[0].pPhysicalAddr; 
  WD_CardRegister(hWD, &cardReg);
  if  (!cardReg.hCard)
  {
      printf("failed mapping dma memory buffer\n");
     // exit
  }
  printf("allocated 0x%x bytes, in physical address 0x%x,"
      "user mode address 0x%x, kernel mode address 0x%x\n",
      dma.dwBytes, dma.Page[0].pPhysicalAddr,
      cardReg.Card.Item[0].I.Mem.dwUserDirectAddr,
      cardReg.Card.Item[0].I.Mem.dwTransAddr);
 
  ////////////////////////////////////////////////////////////////

  // pass the kernel address to the Kernel PlugIn:

  BZERO (kerPlug);
  kerPlug.pcDriverName = KP_DRIVER_NAME;
  WD_KernelPlugInOpen(hWD, &kerPlug);
  if (!kerPlug.hKernelPlugIn)
  {
      printf("failed opening a handle to the Kernel PlugIn\n");
      // exit
  }

  BZERO (kpCall);
  kpCall.hKernelPlugIn = kerPlug.hKernelPlugIn;
  kpCall.dwMessage = YOUR_MESSAGE;
  kpCall.pData = dma.pKernelAddr;  // [or dwTransAddr - for previous versions]
  WD_KernelPlugInCall(hWD, &kpCall);
  
  *********************************************************************