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 the
WDC_PciDeviceOpen()/WDC_PcmciaDeviceOpen()/WDC_IsaDeviceOpen()
convenience wrappers (v7.0.0+), which call WD_CardRegister().
Solutions:
To resolve this problem, you have the following options:
- 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 to
increase 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-mode virtual
address space. In version 7.x+ of WinDriver, you have the option
to prevent this problem by maping 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 the WD_ITEM_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 from
WDC_XXXGetDeviceInfo()/ WD_XXXGetCardInfo() (PCI/PCMCIA),
and set this flag in the dwOptions field of the card's
relevant memory WD_ITEMS structure. Then pass the modified
card structure to WDC_xxxDeviceOpen()/WD_CardRegister()
(see Code Examples below).
NOTE that if you select to set the
WD_ITEM_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]kptAddr /
pCardReg->Card.Item[i].I.Mem.dwTransAddr), 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
dwBytes field 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
dwPhysicalAddr field in order to begin the mapping
at a different offset in the address space.
(For PCI/PCMCIA, the card structure is returned by
WDC_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_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 and
cardItem.Item[i].dwPhysicalAddr 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].dwOptions |=
WD_ITEM_DO_NOT_MAP_KERNEL;
/* Alternatively, modify cardInfo.Item[1].I.Mem.dwBytes,
and perhaps also cardInfo.Item[1].I.Mem.dwPhysicalAddr,
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.dwBytes = 1000;
cardInfo.Item[1].I.Mem.dwPhysicalAddr += 0x10;
*/
...
WDC_PciDeviceOpen(&hDev, &cardInfo, ...);
===========================================================
Example 2: WDC ISA API
WD_CARD card;
...
card.Item[1].dwOptions |= WD_ITEM_DO_NOT_MAP_KERNEL;
/* Alternatively, modify card.Item[1].I.Mem.dwBytes,
and perhaps also card.Item[1].I.Mem.dwPhysicalAddr,
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.dwBytes = 1000;
card.Item[1].I.Mem.dwPhysicalAddr += 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].dwOptions |=
WD_ITEM_DO_NOT_MAP_KERNEL;
/* Alternatively, modify cardInfo.Item[1].I.Mem.dwBytes,
and perhaps also cardInfo.Item[1].I.Mem.dwPhysicalAddr,
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.dwBytes = 1000;
cardInfo.Item[1].I.Mem.dwPhysicalAddr += 0x10;
*/
cardReg.Card = cardInfo.Card;
...
WD_CardRegister(hWD, &cardReg);
===========================================================
Example 4: WD_xxx ISA API
WD_CARD_REGISTER cardReg;
...
cardReg.Card.Item[1].dwOptions |=
WD_ITEM_DO_NOT_MAP_KERNEL;
/* Alternatively, modify cardReg.Item[1].I.Mem.dwBytes,
and perhaps also cardReg.Item[1].I.Mem.dwPhysicalAddr,
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.dwBytes = 1000;
cardReg.Item[1].I.Mem.dwPhysicalAddr += 0x10;
*/
...
WD_CardRegister(hWD, &cardReg);
BACK TO
WD_ITEM_DO_NOT_MAP_KERENL EXPLANATION
BACK TO MAP LESS MEMORY EXPLANATION
|