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
Copyright (C) 1998, Tetradyne Software Inc.