WD_CardRegister() attempts to map physical memory address ranges to both virtual user-mode and kernel-mode address space, by calling the relevant OS functions. If either of these mappings fail, e.g., because there is not enough user/kernel virtual memory to enable the mapping of the entire memory range, WD_CardRegister() will fail and a relevant error message will be printed to the Debug Monitor log, indicating which OS-mapping function failed (see example in Technical Document #73).

You may also encounter the same problem when using theWDC_PciDeviceOpen()/WDC_PcmciaDeviceOpen()/WDC_IsaDeviceOpen() convenience wrappers (v7.0.0+), which call WD_CardRegister().

Solutions:

To resolve this problem, you have the following options:

NOTE
There may be some differences between the API in your version of WinDriver and that used in the following solutions (such as differences in enumeration names and field names and/or types).
  • On NT 4.0 and higher, the number of page table entries for the virtual memory tables is set in the registry. To enable the mapping of a large memory range, you may need toincrease the number of page table entries in the registry, and thus increase the amount of available virtual memory.
    To do this, increase the value of the SystemPages registry entry, found under:
    HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Memory Management\SystemPages
    (Run regedit.exe to open the registry.
    At the end, reboot in order for the changes to take place.)The value to set should be roughly calculated as
    <available memory> / 4K + <enough pages for the rest of the applications>Try setting the number of page table entries to 0x100000. It should be sufficient for most uses.

 

  • When the memory mapping fails, it is most often as a result of insufficient kernel-modevirtual address space. In version 7.x+ of WinDriver, you have the option to prevent this problem by mapping the memory only to the user-mode virtual address space:If the card’s registration function (WDC_XXXDeviceOpen() / WD_CardRegister()) fails to map all the memory on the card to the kernel-mode virtual address space, you can use theWD_ITEM_MEM_DO_NOT_MAP_KERNEL flag to instruct WinDriver to attempt mapping the physical memory only to the user-mode virtual address space, and not to the kernel address space: Modify the WD_CARD structure that you defined (ISA) or received fromWDC_XXXGetDeviceInfo()/ WD_XXXGetCardInfo() (PCI/PCMCIA), and set this flag in theI.Mem.dwOptions field of the card’s relevant memory WD_ITEMS structure. Then pass the modified card structure to WDC_xxxDeviceOpen()/WD_CardRegister() (see Code Examplesbelow).NOTE that if you select to set the WD_ITEM_MEM_DO_NOT_MAP_KERNEL flag, the registration function will not return a kernel-mode mapping of the selected memory item (as usually returned in pDeviceInfo->pAddrDesc[i].pAddr / pCardReg->Card.Item[i].I.Mem.pTransAddr), and you will therefore not be able to rely on this mapping in calls to WinDriver’s APIs or to access the memory directly memory from a Kernel PlugIn driver.

 

  • Another approach is to map a smaller amount of memory. You can modify the qwBytesfield of the relevant memory item, stored in the card’s WD_CARD structure (card.Item[i].I.Mem), before calling WDC_xxxDeviceOpen() or WD_CardRegister(), in order to map a smaller portion of the memory.
    You can also modify the memory item’s pPhysicalAddr field in order to begin the mapping at a different offset in the address space.
    (For PCI/PCMCIA, the card structure is returned byWDC_PciGetDeviceInfo()/WDC_PcmciaGetDeviceInfo() in pDeviceInfo->Card, or by the lower-level WD_PciGetCardInfo()/ WD_PcmciaGetCardInfo() function in pCard->Card).
    For a better understanding, refer to the Code Examples below.NOTE: When using this method, take care not to access any memory addresses on the card that have not been mapped.

Code Examples:

The following code examples demonstrate how to set the WD_ITEM_MEM_DO_NOT_MAP_KERNEL flag, or lock less memory, using the different WinDriver APIs.

NOTE: For the purpose of this example we assume that the information for the relevant memory item is stored in the second item of the card’s items array (i.e., index 1). However, this will not necessarily be the case. For PCI/PCMCIA you should write your code to iterate through the items array, returned by WDC_xxxGetDeviceInfo()/ WD_xxxGetCardInfo(), in order to find the index of the memory item that you wish to modify (card.Item[i].item should be of type ITEM_MEMORY andcardItem.Item[i].pPhysicalAddr should match the physical address of your target memory range).
===========================================================

Example 1: WDC PCI/PCMCIA API

NOTE: For PCMCIA, just replace any “PCI”/”Pci” references below with “PCMCIA”/”Pcmcia”.
    WD_PCI_CARD_INFO cardInfo;
...
WDC_PciGetDeviceInfo(&cardInfo);
    cardInfo.Card.Item[1].I.Mem.dwOptions |=
WD_ITEM_MEM_DO_NOT_MAP_KERNEL;
    /* Alternatively, modify cardInfo.Item[1].I.Mem.qwBytes,
and perhaps also cardInfo.Item[1].I.Mem.pPhysicalAddr,
in order to map only a selected portion of the memory.
For example, to lock 1,000 bytes, beginning with
offset 0x10 of the address space, set:
       cardInfo.Item[1].I.Mem.qwBytes = 1000;
cardInfo.Item[1].I.Mem.pPhysicalAddr += 0x10;
*/
    ...
WDC_PciDeviceOpen(&hDev, &cardInfo, ...);

===========================================================

Example 2: WDC ISA API

    WD_CARD card;
...
    card.Item[1].I.Mem.dwOptions |= WD_ITEM_MEM_DO_NOT_MAP_KERNEL;
    /* Alternatively, modify card.Item[1].I.Mem.qwBytes,
and perhaps also card.Item[1].I.Mem.pPhysicalAddr,
in order to map only a selected portion of the memory.
For example, to lock 1,000 bytes, beginning with
offset 0x10 of the address space, set:
       card.Item[1].I.Mem.qwBytes = 1000;
card.Item[1].I.Mem.pPhysicalAddr += 0x10;
*/
...
WDC_IsaDeviceOpen(&hDev, &card, ...);

===========================================================

Example 3: WD_xxx PCI/PCMCIA API

NOTE: For PCMCIA, just replace any ‘PCI’/’Pci’ references below with ‘PCMCIA’/’Pcmcia’.

    WD_PCI_CARD_INFO cardInfo;
WD_CARD_REGISTER cardReg;
...
WD_PciGetCardInfo(hWD, &cardInfo);
    cardInfo.Card.Item[1].I.Mem.dwOptions |=
WD_ITEM_MEM_DO_NOT_MAP_KERNEL;
    /* Alternatively, modify cardInfo.Item[1].I.Mem.qwBytes,
and perhaps also cardInfo.Item[1].I.Mem.pPhysicalAddr,
in order to map only a selected portion of the memory.
For example, to lock 1,000 bytes, beginning with
offset 0x10 of the address space, set:
       cardInfo.Item[1].I.Mem.qwBytes = 1000;
cardInfo.Item[1].I.Mem.pPhysicalAddr += 0x10;
*/
cardReg.Card = cardInfo.Card;
...
WD_CardRegister(hWD, &cardReg);

===========================================================

Example 4: WD_xxx ISA API

    WD_CARD_REGISTER cardReg;
...
    cardReg.Card.Item[1].I.Mem.dwOptions |=
WD_ITEM_MEM_DO_NOT_MAP_KERNEL;
    /* Alternatively, modify cardReg.Item[1].I.Mem.qwBytes,
and perhaps also cardReg.Item[1].I.Mem.pPhysicalAddr,
in order to map only a selected portion of the memory.
For example, to lock 1,000 bytes, beginning with
offset 0x10 of the address space, set:
       cardReg.Item[1].I.Mem.qwBytes = 1000;
cardReg.Item[1].I.Mem.pPhysicalAddr += 0x10;
*/
...
WD_CardRegister(hWD, &cardReg);