DriverGen Example

Back to DFC Information page...
-------------------------- Rad20.h ---------------------------------------
////////////////////////////////////////////////////////////////////////
// Rad20.h
//
// Interface definition of CR20Device class used for controlling a
// RadiSys EXM20 A/D converter card.
//
// This is an internal driver header file 
//		-don't include it in user-mode apps.
//
// Copyright (C) 1996, Tetradyne Software Inc.
// All rights reserved.

#ifndef RAD20_H
#define RAD20_H

///////////////////////////////////////////////////////////////////////
// CR20Device device class

class CR20Device : public CDevice
{
public:
	CR20Device();

	// CDevice overrides
	NTSTATUS Initialize();
	NTSTATUS DispatchDeviceControl(CIrp* pIrp);
	NTSTATUS DispatchCleanup(CIrp* pIrp);
	BOOLEAN OnInterrupt(PKINTERRUPT pInterrupt);
	VOID StartIo(CIrp* pIrp);
	VOID DpcForIsr(PKDPC pDpc, CIrp* pIrp, PVOID pContext);

	// Misc member functions
	BOOLEAN StartAcquisitionSynch(PVOID pContext);
	BOOLEAN AbortAcquisitionSynch(PVOID pContext);
	NTSTATUS AcquireSample(ULONG nChannel, PULONG pnValue);
	VOID AcquisitionCancel(CIrp* pIrp);

	// Static callback routines
	static VOID AcquisitionCancelCallback(
		PDEVICE_OBJECT pDeviceObject, PIRP pIrp);

	// Device resources
	CInterrupt<CR20Device> m_interrupt;
	CPortRange m_ports;

	// Register offsets from base port address
	enum {AD0=0, AD1=1, SCAN=2, DIO=3, STATUS_A=8, CTRL=9};

	// Misc constants
	enum {BOARD_TIMEOUT=100};
	enum {BOARD_DELAY=20};
	enum {ACQ_PRIORITY_BOOST=2};
	
	// Base port address
	ULONG m_nBase;

	// Pointer to buffer for current request
	PUSHORT m_pBuffer;

	// Number of sample acquired so far for current request
	ULONG m_nSamples;

	// Target number of samples for current request
	ULONG m_nTargetSamples;
};

///////////////////////////////////////////////////////////////////////
// CR20Driver class -based on DFC CIsaDriver

class CR20Driver : public CIsaDriver<CR20Device> {};

#endif // ifndef RAD20_H
-------------------------------- Rad20.cpp ---------------------------------
///////////////////////////////////////////////////////////////////////
// Rad20.cpp
//
// DriverEntry and implementation of CR20Device class (see rad20.h)
//
// Copyright (C) 1996, Tetradyne Software Inc.
// All rights reserved.

// #define NT3DDK

#ifdef DFC_WIN
	// Include DFC headers for Win95 VxD build
	#include "Windfc.h"
	#include "Lcode.h"
#else
	// Include DFC headers for NT driver build
	#include "Dfc.h"
	#include "R20log.h"
#endif

#include "R20intr.h"
#include "Rad20.h"

//////////////////////////////////////////////////////////////////////
// DriverEntry module entry point.

extern "C"
NTSTATUS DriverEntry(
	IN PDRIVER_OBJECT pDriverObject, 
	IN PUNICODE_STRING pRegistryPath)
{
	// Create a driver object
	NTSTATUS status;
	CR20Driver* pDriver = new CR20Driver;

	if (!pDriver)
	{
		status = STATUS_NO_MEMORY;
		DfcLogError(pDriverObject, IO_ERR_INSUFFICIENT_RESOURCES, status);
	}
	else
	{
		// Call driver OnEntry to create device objects from registry
		status = pDriver->OnEntry(pDriverObject, pRegistryPath);

		// Return an error if no devices were created successfully
		if (NT_SUCCESS(status) && !pDriver->m_nDevices)
			status = STATUS_NO_SUCH_DEVICE;

		// Free driver
		delete pDriver;
	}

	return status;
}

///////////////////////////////////////////////////////////////////////
// CR20Device implementation

CR20Device::CR20Device()
{
	// Setup resources for auto-initialization
	AddResource(&m_interrupt);
	AddResource(&m_ports);

	m_pBuffer = NULL;
	m_nSamples = 0;
	m_nTargetSamples = 0;
}

/////////////////// Initialize()
//
// This CDevice::Initialize override is called by the DFC framework
// shortly after device creation to initialize the hardware and the
// resources being used by this device.
//
// For this example, the card is setup to acquire off channel 0
// and interrupt on each external trigger pulse.

NTSTATUS CR20Device::Initialize()
{
	// Call base class to initialize port range and interrupt resources
	NTSTATUS status = CDevice::Initialize();
	if (!NT_SUCCESS(status))
		return status;

	// Store mapped port base for inp/outp
	m_nBase = m_ports.GetBase();

	// Enable interrupt trigger on card
	outp(m_nBase + SCAN, 0x00);

	// Setup mask of interrupt level for board control register
	ULONG nLevel = m_interrupt.m_nRelLevel;
	char chMask;

	if (nLevel >= 3 && nLevel <= 7)
		chMask = (char) nLevel << 4;
	else if (nLevel == 9)
		chMask = 0x20;

	// Set trigger for acquisition on TRIG 0 input signal
	chMask |= 0x02;	
	outp(m_nBase + CTRL, 0x80 | chMask);

	// Turn off all digital outputs on the board
	outp(m_nBase + DIO, 0x00);

	return STATUS_SUCCESS;
}

/////////////////// DispatchDeviceControl()
//
// Top level dispatch routine for DeviceIoControl requests from user-mode 
// components. This routine validates parameters and serializes IRPs
// through the device StartIo routine.

NTSTATUS CR20Device::DispatchDeviceControl(CIrp* pIrp)
{
	NTSTATUS status;
	ULONG cbRet = 0;

	switch (pIrp->GetControlCode())
	{
	case IOCTL_ACQUIRE:

		// Input buffer must have room for channel number
		if (pIrp->GetDiocInBufferLength() < sizeof(ULONG))
			status = STATUS_INVALID_PARAMETER;
		else
		{
			// Output buffer must have room for a sample value
			if (pIrp->GetDiocOutBufferLength() < sizeof(ULONG))
				status = STATUS_INVALID_PARAMETER;
			else
				status = STATUS_PENDING;
		}
		break;

	case IOCTL_INTR_ACQUIRE:

		// Output buffer must be able to at least hold one sample
		if (pIrp->GetDiocOutBufferLength() < sizeof(USHORT))
			status = STATUS_INVALID_PARAMETER;
		else
			status = STATUS_PENDING;
		break;

	default:

		status = STATUS_NOT_IMPLEMENTED;
		break;
	}

	TRACE("CR20Device: In DispatchDeviceControl. IRP(%x,%x). Status=%x\n",
		pIrp, pIrp->GetControlCode(), status);

	// Complete the request on an error or success
	if (status != STATUS_PENDING)
		pIrp->Complete(status, cbRet);
	else
	{
		pIrp->MarkPending();
		StartPacket(pIrp, NULL, DfcDefaultCancel);
	}

	return status;
}

/////////////////// StartIo()
//
// Starts a hardware operation on the board. 

VOID CR20Device::StartIo(CIrp* pIrp)
{
	KIRQL cancelIrql;
	NTSTATUS status;
	ULONG cbRet;

	TRACE("CR20Device: Entering StartIo with IRP(%x,%x)\n",
		pIrp, pIrp->GetControlCode());

	if (DfcIrpCancelled(pIrp))
	{
		// IRP has been cancelled, so just return
		TRACE("CR20Device: StartIo returning with cancelled IRP(%x,%x)\n",
			pIrp, pIrp->GetControlCode());
		return;
	}

	switch (pIrp->GetControlCode())
	{
	case IOCTL_ACQUIRE:

		// Acquire the sample
		status = AcquireSample(
			*((PULONG)(pIrp->GetDiocInBuffer())),
			(PULONG)(pIrp->GetDiocOutBuffer()));
		cbRet = (NT_SUCCESS(status) ? sizeof(ULONG) : 0);
				
		// Start next packet and complete this one (note order)
		StartNextPacket(TRUE);
		pIrp->Complete(status, cbRet);
		break;

	case IOCTL_INTR_ACQUIRE:

		// Synchronize with ISR to setup shared state variables
		m_interrupt.Synchronize(this, StartAcquisitionSynch, pIrp);

		// The IRP is going to be left pending, so reset cancel routine
		IoAcquireCancelSpinLock(&cancelIrql);
		IoSetCancelRoutine(pIrp, AcquisitionCancelCallback);
		IoReleaseCancelSpinLock(cancelIrql);
		break;

	default:
		break;
	}
}

/////////////////// StartAcquisitionSynch()
//
// This routine runs synchronized with the ISR at raised IRQL. It 
// starts an acquisition IRP passed as the pContext parameter. 

BOOLEAN CR20Device::StartAcquisitionSynch(PVOID pContext)
{
	ASSERT(pContext);
	CIrp* pIrp = (CIrp*) pContext;

	// Set acquisition state variables
	m_pBuffer = (PUSHORT) pIrp->GetDiocOutBuffer();
	m_nSamples = 0;
	m_nTargetSamples = pIrp->GetDiocOutBufferLength()/sizeof(USHORT);

	// Enable interrupts on the board
	outp(m_nBase + CTRL, (inp(m_nBase + CTRL) | 0x80));
	outp(m_nBase + STATUS_A, 0x00);

	return TRUE;
}

/////////////////// AcquireSample()
//
// This routine acquires one sample from a given channel.
//
// Return Value:
//
//		Returns STATUS_SUCCESS or STATUS_IO_TIMEOUT.

NTSTATUS CR20Device::AcquireSample(ULONG nChannel, PULONG pnValue)
{
	ULONG nDelay;
	ASSERT(pnValue);

	// Set start and end channel to nChannel and let board settle
	outp(m_nBase + SCAN, (UCHAR)(nChannel << 4 | nChannel));  
	KeStallExecutionProcessor(BOARD_DELAY);
	
	// Initiate the conversion
	outp(m_nBase + AD0, 0x00);
	KeStallExecutionProcessor(BOARD_DELAY);

	// Poll for completion of conversion
	for (nDelay =0; nDelay < BOARD_TIMEOUT; nDelay++)
		if (!(inp(m_nBase + STATUS_A)) & 0x80)
			break;

	if (nDelay >= BOARD_TIMEOUT)
		return STATUS_IO_TIMEOUT;

	// Read the 12-bit sample value
	UCHAR lsb = inp(m_nBase + AD0);
	UCHAR msb = inp(m_nBase + AD1);
	*pnValue = msb << 4 | lsb >> 4;

	return STATUS_SUCCESS;
}

/////////////////// OnInterrupt()
//
// Device ISR. Reads a sample and schedules a DPC to complete the current
// request if the number of samples acquired equals the requested number.

BOOLEAN CR20Device::OnInterrupt(PKINTERRUPT pInterrupt)
{
	if (m_pBuffer)
	{
		// Get channel 0 sample and store in buffer
		char chLsb = inp(m_nBase + AD0);
		char chMsb = inp(m_nBase + AD1);
		m_pBuffer[m_nSamples] = chMsb << 4 | chLsb >> 4;

		// Increment sample count and compare to target
		if (++m_nSamples >= m_nTargetSamples)
		{
			// This request is done. Disable interrupts on the card.
			outp(m_nBase + CTRL, (inp(m_nBase + CTRL) & 0x7F));

			// Set bytes returned and status in current IRP
			CIrp* pIrp = GetCurrentIrp();
			pIrp->SetCbReturned(m_nSamples * sizeof(USHORT));
			pIrp->SetReturnStatus(STATUS_SUCCESS);

			// Schedule DPC to complete pending request
			RequestDpc(GetCurrentIrp(), 0);
			m_pBuffer = NULL;
		}
	}

	return TRUE;
}

/////////////////// DpcForIsr()
//
// Base class DpcForIsr sets cancel routine of given IRP to NULL and 
// completes the IRP with a priority boost of 2. It is expected that 
// the IRP status and information have already been set.

VOID CR20Device::DpcForIsr(PKDPC pDpc, CIrp* pIrp, PVOID pContext)
{
	if (pIrp)
	{
		// Check if IRP has been cancelled
		if (DfcIrpCancelled(pIrp))
		{
			TRACE("CR20: Cancelled IRP(%x) in DpcForIsr.\n", pIrp);
			return;
		}

		// Start next request and complete this one
		TRACE("CR20: Completing IRP(%x,%x) in DpcForIsr. s=%x\n",
			pIrp, pIrp->GetControlCode(), pIrp->GetReturnStatus());
		StartNextPacket(TRUE);
		pIrp->Complete(ACQ_PRIORITY_BOOST);
	}
}
Back to DFC Information page...

Home | Products | Support | Order | Press