And so, if I have to program my 32-bit only device, I have to use sglist.Element[0].Address.LowPart that corresponds to the physical address translated by the framework, correct ?
You’ve raised a couple of brand-new issues here. Is this a very old device? Because any modern hardware designer who creates a PCIe device that is limited to 32-bit addressing is guilty of malpractice. Windows has had 64-bit physical addresses since the very beginning, clear back in the 20th Century. ALL current PCIe IP blocks supports 64-bit physical addresses. There’s no excuse.
When your DMA is limited to 32 bits, the system has to take an extra step. You can’t control where the user’s buffer lives, and since modern systems often have 16GB or 32GB or more of RAM, the average user-mode buffer these days is statistically going to be above the 4GB mark. In that case, the operating system has to allocate special space below the physical 4GB limit. These are called “bounce buffers”. When the user submits a request, the I/O system will copy his buffer into a “bounce buffer” before calling your EvtProgramDma callback. There are a limited number of “bounce buffers”, which means your user request might be chopped into several pieces, with each piece getting another call to EvtProgramDma. This is mostly done without your knowledge, but since you have to maintain a destination address, this may be something you need to know.
Peter is quite right to point out the difference between “physical” and “logical” addresses. A bus address is not necessarily the same as a physical address. I admit to being lax with this terminology, because in my career I have never encountered a system where the two were not identical.
In my OP’s case, I want to perform DMA transfer using 1 IOCTL in User App for programming my device and I wonder if this is the good method because all PCI examples use WriteFile() method.
There’s almost no difference. It may not be obvious from above, but from a driver standpoint, ReadFile, WriteFile and DeviceIoControl are all virtually identical. The driver just gets an IRP, and the buffers are stored in the same places. In YOUR case, there is an additional consideration, because you need to specify a destination address. With DeviceIoControl, you have the opportunity to send two buffers. You can put the address in buffer 1, and the data in buffer 2. Without that, you have to invent some other scheme, and it doesn’t seem as natural.