Hi again,
xxxxx@sedsystems.ca wrote:
I have to write a windows driver (for vista and XP) that matches an already written linux driver for a PCI card with memory mapped registers. The linux driver maps the memory registers into the kernel driver, which I understand can be easily done in KMDF or even WDF. The linux driver also maps a portion of these same registers to it's userspace driver using mmap. My windows driver will have to do the same thing. The goal was to simplify the kernel driver in linux and move some complexity to userspace, thus making the kernel-mode driver more portable.
Can this be done in KMDF (map card memory registers that have already been mapped into a kernel driver, out to userspace) and are there coding examples?
Questions? First check the Kernel Driver FAQ at http://www.osronline.com/article.cfm?id=256
To unsubscribe, visit the List Server section of OSR Online at ListServer/Forum
a sample code from my driver:
typedef struct _MMIO_RING_0_MAP{
PVOID sysAddrBase; // the starting system virtual address of
the mapped physical address range
ULONG size; // size of the mapped physical address range
PVOID usermodeAddrBase; // pointer to the usermode virtual address
where this range is mapped
PMDL pMdl; // Memory Descriptor List for the memory
mapped I/O range to be mapped
}MMIO_RING_0_MAP, *PMMIO_RING_0_MAP;
typedef struct _DEVICE_EXTENSION{
MMIO_RING_0_MAP mapZone[MAX_MAPPED_MMIO];
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
..................
NTSTATUS MapMmio(PDEVICE_OBJECT pDO, PIRP pIrp)
/*++
Routine Description:
Process the IRPs with IOCTL_MAP_MMIO code.
This routine maps a physical address range
to the usermode application address space.
Arguments:
pDO - pointer to the device object of this driver.
pIrp - pointer to an I/O Request Packet.
Return Value:
NT Status code
Notes:
This function can only map the area
below the 4-GB limit.
--*/
{
PDEVICE_EXTENSION pDevExt;
PHYSICAL_ADDRESS phyAddr;
PMMIO_MAP pUsermodeMem;
ULONG i, free_idx;
pDevExt = (PDEVICE_EXTENSION) pDO->DeviceExtension;
//
// Check for free mapZone in the device extension.
// If none is free, return an error code.
//
for(i = 0; i < MAX_MAPPED_MMIO; i++)
{
if( pDevExt->mapZone[i].sysAddrBase == NULL )
{
free_idx = i;
break;
}
}
if( i == MAX_MAPPED_MMIO )
{
return STATUS_INVALID_DEVICE_REQUEST;
}
//
// We have obtained a free mapZone, map the physical address range.
//
pUsermodeMem = (MMIO_MAP*) MmGetSystemAddressForMdlSafe(
pIrp->MdlAddress, NormalPagePriority );
if( NULL == pUsermodeMem) {
return STATUS_INVALID_USER_BUFFER;
}
phyAddr.HighPart = 0;
phyAddr.LowPart = pUsermodeMem->phyAddrStart;
pDevExt->mapZone[free_idx].sysAddrBase = MmMapIoSpace( phyAddr,
pUsermodeMem->size, MmNonCached);
if(NULL == pDevExt->mapZone[free_idx].sysAddrBase)
{
return STATUS_BUFFER_TOO_SMALL;
}
pDevExt->mapZone[free_idx].pMdl =
IoAllocateMdl(pDevExt->mapZone[free_idx].sysAddrBase,
pUsermodeMem->size, FALSE, FALSE, NULL);
if(NULL == pDevExt->mapZone[free_idx].pMdl)
{
MmUnmapIoSpace(pDevExt->mapZone[free_idx].sysAddrBase,
pUsermodeMem->size);
pDevExt->mapZone[free_idx].sysAddrBase = NULL;
return STATUS_BUFFER_TOO_SMALL;
}
pDevExt->mapZone[free_idx].size = pUsermodeMem->size;
//
// Map the system virtual address to usermode virtual address
//
MmBuildMdlForNonPagedPool(pDevExt->mapZone[free_idx].pMdl);
pDevExt->mapZone[free_idx].usermodeAddrBase =
MmMapLockedPagesSpecifyCache( pDevExt->mapZone[free_idx].pMdl,
UserMode, MmNonCached,
NULL, FALSE,
NormalPagePriority);
if(NULL == pDevExt->mapZone[free_idx].usermodeAddrBase)
{
IoFreeMdl(pDevExt->mapZone[free_idx].pMdl);
MmUnmapIoSpace(pDevExt->mapZone[free_idx].sysAddrBase,
pDevExt->mapZone[free_idx].size);
pDevExt->mapZone[free_idx].sysAddrBase = NULL;
pDevExt->mapZone[free_idx].size = 0;
return STATUS_BUFFER_TOO_SMALL;
}
// copy the resulting usermode virtual address to IRP "buffer"
pUsermodeMem->usermodeVirtAddr =
pDevExt->mapZone[free_idx].usermodeAddrBase;
return STATUS_SUCCESS;
}
NTSTATUS CleanupMmioMapping(PDEVICE_EXTENSION pDevExt, ULONG i)
/*++
Routine Description:
This routine cleanup the mapping of a MMIO range
and resources it consumes.
Arguments:
pDevExt - pointer to the device extension of the driver
i - index of the mapZone to cleanup
Return Value:
NT Status code
--*/
{
if( NULL != pDevExt->mapZone[i].usermodeAddrBase )
{
MmUnmapLockedPages( pDevExt->mapZone[i].usermodeAddrBase,
pDevExt->mapZone[i].pMdl);
pDevExt->mapZone[i].usermodeAddrBase = NULL;
}
if( NULL != pDevExt->mapZone[i].pMdl )
{
IoFreeMdl(pDevExt->mapZone[i].pMdl);
pDevExt->mapZone[i].pMdl = NULL;
}
if( NULL != pDevExt->mapZone[i].sysAddrBase )
{
MmUnmapIoSpace( pDevExt->mapZone[i].sysAddrBase,
pDevExt->mapZone[i].size);
pDevExt->mapZone[i].sysAddrBase = NULL;
pDevExt->mapZone[i].size = 0;
}
return STATUS_SUCCESS;
}
NTSTATUS UnmapMmio(PDEVICE_OBJECT pDO, PIRP pIrp)
/*++
Routine Description:
Process the IRPs with IOCTL_UNMAP_MMIO code.
This routine unmaps a previously mapped physical
address range.
Arguments:
pDO - pointer to the device object of this driver.
pIrp - pointer to an I/O Request Packet.
Return Value:
NT Status code
Notes:
This function can only unmap the area
below the 4-GB limit.
--*/
{
PDEVICE_EXTENSION pDevExt;
PMMIO_MAP pMmioMap;
ULONG i;
//
// Unmap the requested zone from the system address space
// and update the device extension data
//
pDevExt = (PDEVICE_EXTENSION) pDO->DeviceExtension;
pMmioMap = (PMMIO_MAP) MmGetSystemAddressForMdlSafe(
pIrp->MdlAddress, NormalPagePriority );
for(i = 0 ; i < MAX_MAPPED_MMIO; i++)
{
if(pDevExt->mapZone[i].usermodeAddrBase == pMmioMap->usermodeVirtAddr)
{
CleanupMmioMapping(pDevExt, i);
break;
}
}
return STATUS_SUCCESS;
}
NTSTATUS
DispatchIoControl(
IN PDEVICE_OBJECT pDO,
IN PIRP pIrp
)
/*++
Routine Description:
Io control code dispatch routine
Arguments:
DeviceObject - pointer to a device object.
Irp - pointer to current Irp
Return Value:
NT status code.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(pIrp);
switch(irpStack->Parameters.DeviceIoControl.IoControlCode)
{
......
case IOCTL_MAP_MMIO:
{
if(irpStack->Parameters.DeviceIoControl.InputBufferLength >=
sizeof(MMIO_MAP)) {
status = MapMmio(pDO, pIrp);
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
}break;
case IOCTL_UNMAP_MMIO:
{
if(irpStack->Parameters.DeviceIoControl.InputBufferLength >=
sizeof(MMIO_MAP)) {
status = UnmapMmio(pDO, pIrp);
} else {
status = STATUS_BUFFER_TOO_SMALL;
}
}break;
default:
{
status = STATUS_INVALID_DEVICE_REQUEST;
}break;
}
//
// complete the I/O request and return appropriate values
//
pIrp->IoStatus.Status = status;
// Set number of bytes to copy back to user-mode
if(status == STATUS_SUCCESS)
{
pIrp->IoStatus.Information =
irpStack->Parameters.DeviceIoControl.OutputBufferLength;
}
else
{
pIrp->IoStatus.Information = 0;
}
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
return status;
}
I hope it help in some way 
Cheers,
Darmawan