PCI device with no DMA support

Hello All,

For quite some time, I am trying to implement a PCI driver using KMDF. I even went through all the examples that are given in the WDK for PCI devices, but all of them assume in-build DMA support, which my device doesn’t have.

In particular, I have an FPGA core on which we have burned some PCI devices. These devices are very basic in terms of functionality and do not have DMA or even interrupt support. Can you suggest me a very basic PCI driver example or code or methodology that I can use for interaction with my device.

I am also trying my hands at WDM. Please point me to a good example or reading material which can provide me a concrete point to start for the driver development of my device.

Thanks.

I don’t see why DMA support in the sample driver is a problem. Just rip out all of the DMA API calls. What remains is what you probably can use, either io port access or memory mapped io

d

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Friday, November 05, 2010 1:57 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] PCI device with no DMA support

Hello All,

For quite some time, I am trying to implement a PCI driver using KMDF. I even went through all the examples that are given in the WDK for PCI devices, but all of them assume in-build DMA support, which my device doesn’t have.

In particular, I have an FPGA core on which we have burned some PCI devices. These devices are very basic in terms of functionality and do not have DMA or even interrupt support. Can you suggest me a very basic PCI driver example or code or methodology that I can use for interaction with my device.

I am also trying my hands at WDM. Please point me to a good example or reading material which can provide me a concrete point to start for the driver development of my device.

Thanks.


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

Thanks for the prompt reply. I actually tried doing what you have suggested here but doing so makes the driver code completely void of read/write functionality…Can you provide me some type of read/write function which can be used in this case…??

Basically my device shows 1 CmResourceTypeMemory in its ResourceList… But how I can use it to map in software and use for reading/writing purpose is something i m not able to comprehend.If you can put some light in this regard or point me to a good reading resource to understand this, it would be really appreciated. Besides my device doesnt have interrupt functionality as well…this also is adding to the woes…So i will really require your help in this case…

Thanks.

xxxxx@gmail.com wrote:

Thanks for the prompt reply. I actually tried doing what you have suggested here but doing so makes the driver code completely void of read/write functionality…Can you provide me some type of read/write function which can be used in this case…??

I can’t imagine where you are looking. Virtually every PCI example in
the WDK includes an example of how to map a BAR resource into memory.
Once the resource is mapped, your device simply appears as a chunk of
memory. You can access it using normal C pointers, although the better
method is to use READ_REGISTER_ULONG and WRITE_REGISTER_ULONG.

Basically my device shows 1 CmResourceTypeMemory in its ResourceList… But how I can use it to map in software and use for reading/writing purpose is something i m not able to comprehend.If you can put some light in this regard or point me to a good reading resource to understand this, it would be really appreciated. Besides my device doesnt have interrupt functionality as well…this also is adding to the woes…So i will really require your help in this case…

See, for example, src\general\pcidrv\kmdf\hw\nic_init.c. It handles a
CmResourceTypeMemory resource by calling MmMapIoSpace. The thing it
returns is a kernel pointer. You pass that address plus the offset of
the register you want to read into READ_REGISTER_ULONG.

What kind of a device is this?


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Hmmmm…

Here’s an example of a KMDF driver for the world’s simplest Programmed I/O (that is, non-DMA) PCI-based device. You can even BUY the device at our cost:

http://www.osronline.com/article.cfm?article=403

Peter
OSR

xxxxx@gmail.com wrote:

Thanks for the prompt reply. I actually tried doing what you have suggested
here but doing so makes the driver code completely void of read/write
functionality…Can you provide me some type of read/write function which
can be used in this case…??

Basically my device shows 1 CmResourceTypeMemory in its ResourceList…
But how I can use it to map in software and use for reading/writing purpose
is something i m not able to comprehend.If you can put some light in this
regard or point me to a good reading resource to understand this, it would
be really appreciated. Besides my device doesnt have interrupt functionality
as well…this also is adding to the woes…So i will really require your
help in this case…

It sounds as if your problem is perhaps with knowing how to drive your
device rather than writing a driver to do so. You’ve told us that the
device has memory. What do you want to do with that memory?

Thanks Peter,

RE: PCI device with no DMA support
Hmmmm… Here’s an example of a KMDF driver for the world’s simplest Programmed I/O (that is, non-DMA) PCI-based device. You can even BUY the device at our cost: http://www.osronline.com/article.cfm?article=403 Peter OSR

I actually tried this code and my driver is attaching fine…It’s only when i am trying to access the driver with the test application that you have given here, it’s giving me problem in the CreateFile with error code 0x2 which translates to “The system cannot find the file specified.”… Any idea about the problem?

Thanks.

>code 0x2 which translates to “The system cannot find the file specified.”

What name are you passing to CreateFile?

Try using PnP device interfaces - in user mode, this is SetupDiGetClassDevs+SetupDiGetDeviceInterfaceDetail.


Maxim S. Shatskih
Windows DDK MVP
xxxxx@storagecraft.com
http://www.storagecraft.com

Show us the code that you’re using in WdfDeviceAdd to create the device and assign it a name.

Show us the application code that you’re using to attempt to open the device.

To disagree with Max for a moment: I suggest you forget using PnP device interfaces for now… if you can’t get the CreateFile to work, you’re SURELY not going to be able to get the code for opening a device interface to work.

Peter
OSR

Thanks Peter…Here is my code for the device add…

NTSTATUS
DioEvtDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT DeviceInit)
{
NTSTATUS status = STATUS_SUCCESS;
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
WDF_OBJECT_ATTRIBUTES objAttributes;
WDFDEVICE device;
PDIO_DEVICE_CONTEXT devContext;
WDF_IO_QUEUE_CONFIG ioCallbacks;
//WDF_INTERRUPT_CONFIG interruptConfig;
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
WDF_FILEOBJECT_CONFIG fileObjectConfig;

DECLARE_CONST_UNICODE_STRING(ntDeviceName, L"\Device\WDFDIO");
DECLARE_CONST_UNICODE_STRING(dosDeviceName, L"\DosDevices\WDFDIO");

//
// Initialize the PnpPowerCallbacks structure.
//
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);

//
// Setup the callbacks to manage our hardware resources.
//
// Prepare is called at START_DEVICE time
// Release is called at STOP_DEVICE or REMOVE_DEVICE time
//
pnpPowerCallbacks.EvtDevicePrepareHardware = DioEvtPrepareHardware;
pnpPowerCallbacks.EvtDeviceReleaseHardware = DioEvtReleaseHardware;

//
// These two callbacks set up and tear down hardware state that must be
// done every time the device moves in and out of the D0-working state.
//
pnpPowerCallbacks.EvtDeviceD0Entry= DioEvtDeviceD0Entry;
pnpPowerCallbacks.EvtDeviceD0Exit = DioEvtDeviceD0Exit;

//
// Register the PnP and power callbacks. Power policy related callbacks will
// be registered later in SotwareInit.
//
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);

//
// Create our Device Object and its associated context
//
WDF_OBJECT_ATTRIBUTES_INIT(&objAttributes);

//
// Associate our device context structure type with our WDFDevice
//
WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&objAttributes, DIO_DEVICE_CONTEXT);

//
// Other attributes we might set are EvtCleanupCallback (called when
// the object is deleted) and EvtDestroyCallback (called when the object’s
// references go to zero)…
//

//
// Set the synch scope to device so that child objects such as queue
// and DpcForIsr can inherit the device-level synchronization. By
// doing so, we make sure that queue dispatch callbacks for ioctl,
// cancel-routine, and DpcForIsr are all synchronized with the
// same device-level spinlock provided by the framework. This enables
// us to access global resources among these callbacks without
// worrying about synchronizing access to them with our own lock.
//
objAttributes.SynchronizationScope = WdfSynchronizationScopeDevice;

//
// We want our device object NAMED, thank you very much
//
status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName);

if (!NT_SUCCESS(status)) {

TraceEvents(TRACE_LEVEL_INFORMATION, AMCC_TRACE_INIT,“WdfDeviceInitAssignName failed 0x%0x\n”, status);

return(status);
}

//
// Create the device now
//
status = WdfDeviceCreate(&DeviceInit, // Device Init structure
&objAttributes, // Attributes for WDF Device
&device); // returns pointer to new WDF Device

if ( !NT_SUCCESS(status)) {

TraceEvents(TRACE_LEVEL_INFORMATION, AMCC_TRACE_INIT,“WdfDeviceInitialize failed 0x%0x\n”, status);

return(status);
}

//
// Device creation is complete
//

//
// Create a symbolic link for the control object so that usermode can open
// the device.
//
status = WdfDeviceCreateSymbolicLink(device, &dosDeviceName);

if (!NT_SUCCESS(status)) {

TraceEvents(TRACE_LEVEL_INFORMATION, AMCC_TRACE_INIT,“WdfDeviceCreateSymbolicLink failed 0x%0x\n”, status);

return(status);
}

//
// Configure our queue of incoming requests
//
// We only use the default queue, and we only support IRP_MJ_DEVICE_CONTROL.
// Not supplying a callback results in the request being completed
// with STATUS_NOT_SUPPORTED. So, note that this driver does not support
// either IRP_MJ_READ or IRP_MJ_WRITE.
//
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioCallbacks,
WdfIoQueueDispatchSequential);

ioCallbacks.EvtIoDeviceControl = DioEvtDeviceControl;

status = WdfIoQueueCreate(device,
&ioCallbacks,
WDF_NO_OBJECT_ATTRIBUTES,
NULL); // optional pointer to receive queue handle

if (!NT_SUCCESS(status)) {

TraceEvents(TRACE_LEVEL_INFORMATION, AMCC_TRACE_INIT,“WdfIoQueueCreate for default queue failed 0x%0x\n”, status);

return(status);
}

//
// Get our device context
//
devContext = DioGetContextFromDevice(device);

//
// Store a pointer to our WDF Device
//
devContext->WdfDevice = device;

//
// Create an interrupt object that will later be associated with the
// device’s interrupt resource and connected by the Framework.
//
/*WDF_OBJECT_ATTRIBUTES_INIT(&objAttributes);
WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&objAttributes, INTERRUPT_DATA);

//
// Configure the Interrupt object
//
WDF_INTERRUPT_CONFIG_INIT(&interruptConfig,
DioIsr,
DioDpc);

interruptConfig.AutomaticSerialization = TRUE;

interruptConfig.EvtInterruptEnable = DioEvtInterruptEnable;
interruptConfig.EvtInterruptDisable = DioEvtInterruptDisable;

status = WdfInterruptCreate(device,
&interruptConfig,
&objAttributes,
&devContext->WdfInterrupt);
if (!NT_SUCCESS (status)) {

TraceEvents(TRACE_LEVEL_INFORMATION, AMCC_TRACE_INIT,“WdfInterruptCreate failed 0x%0x\n”, status);

return status;
}*/

//
// Initialize our idle policy
// We accept ALL the defaults here, meaning that our device will idle in
// D3 when it’s not busy… after 5 seconds. Also, we allow WDF to create
// a Device Manager property sheet that’ll allow an appropriately priv’d
// user to determine whether or not the device should power-off idle.
//
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings,
IdleCannotWakeFromS0);

status = WdfDeviceAssignS0IdleSettings(device, &idleSettings);

if (!NT_SUCCESS(status)) {

TraceEvents(TRACE_LEVEL_INFORMATION, AMCC_TRACE_INIT,“WdfDeviceUpdateS0IdleSettings failed 0x%0x\n”, status);

return status;
}

return(status);
}

Here is the application code for opening the device

{
HANDLE DriverHandle;
DWORD code;
ULONG index;
ULONG function;

// Open the device
//
DriverHandle = CreateFile(“\\.\WDFDIO”, // Name of the “device” to open
GENERIC_READ|GENERIC_WRITE, // Access rights requested
0, // Share access - NONE
0, // Security attributes - not used!
OPEN_EXISTING, // Device must exist to open it.
FILE_FLAG_OVERLAPPED, // Open for overlapped I/O
0); // extended attributes - not used!

//
// If this call fails, check to figure out what the error is and report it.
//
if (DriverHandle == INVALID_HANDLE_VALUE) {

code = GetLastError();

printf(“CreateFile failed with error 0x%x\n”, code);

return(code);
}
}

In the CreateFile function, I tried giving "\\.\Device\WDFDIO " and “\Device\WDFDIO” also but that too didnt work…

Thanks for the help.

xxxxx@gmail.com wrote:

Thanks Peter…Here is my code for the device add…

NTSTATUS
DioEvtDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT DeviceInit)
{

DECLARE_CONST_UNICODE_STRING(ntDeviceName, L"\Device\WDFDIO");
DECLARE_CONST_UNICODE_STRING(dosDeviceName, L"\DosDevices\WDFDIO");

//
// Create a symbolic link for the control object so that usermode can open
// the device.
//
status = WdfDeviceCreateSymbolicLink(device, &dosDeviceName);

// Open the device
//
DriverHandle = CreateFile(“\\.\WDFDIO”, // Name of the “device” to open
GENERIC_READ|GENERIC_WRITE, // Access rights requested

In the CreateFile function, I tried giving "\\.\Device\WDFDIO " and “\Device\WDFDIO” also but that too didnt work…

That all looks more or less correct. What error code do you see?


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

I see error code 0x2…which means “The system cannot find the file specified”

My inf file is in the following way…

[Version]
Signature = “$WINDOWS NT$”
Class = Media
ClassGuid = {4d36e96c-e325-11ce-bfc1-08002be10318}
Provider = %MFG%
DriverVer=12/03/2010,6.1.7600.16385
CatalogFile = KmdfSamples.cat

[Manufacturer]
%MSFT%=MSFT,NTx86

[DestinationDirs]
DefaultDestDir = 10,System32\Drivers, CoInstaller_CopyFiles = 11

;-------------------------------------------------------------------------
; Device Install Section
;-------------------------------------------------------------------------
[ControlFlags]
ExcludeFromSelect = *

[Manufacturer]
%MSFT%=MSFT,NT$ARCH$

[SourceDisksFiles]
pcidrv.sys = 1

[SourceDisksNames]
1=%DISK_NAME%,

; For Win2K
[MSFT]
; DisplayName Section DeviceId
; ----------- ------- --------
%PCIDRV.DRVDESC%=PCIDRV_Inst, PCI\VEN_1556&DEV_5550
%PCIDRV.DRVDESC%=PCIDRV_Inst, PCI\VEN_1556&DEV_5551
%PCIDRV.DRVDESC%=PCIDRV_Inst, PCI\VEN_1556&DEV_5552
%PCIDRV.DRVDESC%=PCIDRV_Inst, PCI\VEN_1556&DEV_5553

; For XP and later
[MSFT.NTx86]
; DisplayName Section DeviceId
; ----------- ------- --------
%PCIDRV.DRVDESC%=PCIDRV_Inst, PCI\VEN_1556&DEV_5550
%PCIDRV.DRVDESC%=PCIDRV_Inst, PCI\VEN_1556&DEV_5551
%PCIDRV.DRVDESC%=PCIDRV_Inst, PCI\VEN_1556&DEV_5552
%PCIDRV.DRVDESC%=PCIDRV_Inst, PCI\VEN_1556&DEV_5553

[PCIDRV_Inst.NT]
CopyFiles = PCIDRV.CopyFiles

[PCIDRV.CopyFiles]
pcidrv.sys

[PCIDRV_Inst.NT.Services]
AddService = PCIDRV,0x00000002,PCIDRV_Service

[PCIDRV_Service]
DisplayName = %PCIDRV.SVCDESC%
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
StartType = 3 ; SERVICE_DEMAND_START
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
ServiceBinary = %12%\pcidrv.sys

;-------------------------------------------------------------------------
; WDF Coinstaller installation
;-------------------------------------------------------------------------
[DestinationDirs]
CoInstaller_CopyFiles = 11

[PCIDRV_Inst.NT.CoInstallers]
AddReg = CoInstaller_AddReg
CopyFiles = CoInstaller_CopyFiles

[CoInstaller_CopyFiles]
WdfCoInstaller01009.dll

[SourceDisksFiles]
WdfCoInstaller01009.dll = 1 ; make sure the number matches with SourceDisksNames

[CoInstaller_AddReg]
HKR,CoInstallers32,0x00010000, “WdfCoInstaller01009.dll,WdfCoInstaller”

[PCIDRV_Inst.NT.Wdf]
KmdfService = PCIDRV, PCIDRV_wdfsect

[PCIDRV_wdfsect]
KmdfLibraryVersion = 1.9

;------------------------------------------------------------------------------
; String Definitions
;------------------------------------------------------------------------------

[Strings]
MSFT = “Microsoft”
MFG = “Nuon-Inc”
ClassName = “SampleDevice”
PCIDRV.SVCDESC = “Service for PLDADevice”
PCIDRV.DRVDESC = “PLDADevice”
DISK_NAME = “PLDAInstallationDisk”

And source file infollowing way…

TARGETNAME=PCIDRV
TARGETTYPE=DRIVER

KMDF_VERSION_MAJOR=1

TARGETLIBS=$(TARGETLIBS) \
$(DDK_LIB_PATH)\ntstrsafe.lib

INF_NAME=pcidrv
NTTARGETFILE0=$(OBJ_PATH)$(O)$(INF_NAME).inf
PASS0_BINPLACE=$(NTTARGETFILE0)

INCLUDES=$(INCLUDES) \ .

SOURCES= pcidrv.c

Thanks…

xxxxx@gmail.com wrote:

I see error code 0x2…which means “The system cannot find the file specified”

And do you see your device listed in Device Manager, so that you have
some evidence it is really being loaded?

Did you tell us which operating system? Are you running as an
administrator?


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Yes my device is being shown as one of the entries in the device manager…OS is Windows XP and i m t admin user…