Version 4 |
Hardware Control for 32-bit Windows Applications
Using DriverX, Windows developers can quickly and easily write Windows applications to control a wide range of hardware devices. DriverX is currently being used in applications to control hardware ranging from parallel devices with custom protocols, to board level hardware such as digital I/O cards and servo-controllers.
DriverX, first shipped in 1994, is currently in use in thousands of applications worldwide ranging from motion controllers and A/D converters to custom parallel port devices and dongles.
The DriverX provides basic high-level routines for...
I/O
port access
Physical
memory mapping
Interrupt
handlers
DriverX can be used with common development environments including Visual C++, Borland C++, Visual Basic and Delphi. Executables developed using DriverX can be run on both Windows NT and Windows 95 and can be redistributed royalty free. Since no kernel-mode or DDK development is required, DriverX is very easy to use.
Port Operation Queuing
DriverX provides great performance for many applications through the DriverX port operation queuing routines. Batches of port operations can be queued and passed to the DriverX kernel-mode device driver for execution. Data read and write loops can be executed entirely in kernel-mode using DriverX routines such as QueueLoopBack, QueueBufInp/Outp, and QueueWhileInpAnd.
What's Included?
DriverX includes the following:
Generic
driver(s) - These kernel-mode Windows 95 and Windows NT drivers
provide the basic system services required by the library to
implement hardware operations. The library determines the Windows
platform and loads the appropriate driver at runtime
Routines for port access, physical memory access, and interrupt service routines
Port I/O (including high-performance auto-increment and handshaking read/write support)
LPT Port I/O (including port locking)
Fast memory I/O
High Speed Interrupt handling (worst case interrupt latencies around 15 micro seconds using DriverX on 166Mhz Pentium, 64M RAM running Windows NT4 and Windows 98).
Routines for registry configuration of user devices and the DriverX device drivers
A
utility application to aid the developer in creating registry device configurations
Full
online and printed help
Examples
illustrating every major product feature
Hardware Viewer utility application
C routines and C++ classes with source code
Static library builds
DLL builds and Visual Basic interface module
ActiveX(OLE Control) - This control will appear on the control container's toolbar as a hardware card icon. It can be dragged onto an application form and its methods can be used to control the target hardware.
PCI device support including busmaster DMA
Windows 95/98 Plug and Play support
Binary compatibility between Windows NT/2000/XP and Windows 95/98/ME
Supports Delphi, VB, Visual C++, Borland C++/C++ Builder, and other C/C++ Win32 compilers
Note that Visual Basic users can control port and physical memory ranges using either the C/C++ DLL libraries or the ActiveX (OLE Control). However, only the ActiveX control provides interrupt handling via the ActiveX event firing mechanism.
DriverX Advantages
Port-op queuing - Developers can easily incorporate queuing of port operations for higher throughput. No macro preprocessor is required to use this feature.
Hardware Viewer Utility - With the Hardware Viewer utility, developers can begin interracting with their hardware minutes after installation of the DriverX library package-and without writing a line of code.
C, C++, Delphi, and Visual Basic support in one package with similar APIs.
No undocumented calls. Unlike some other similar development tools and freeware tools on the market, DriverX does not use undocumented NT kernel calls. Using these tools could leave you with an application that won't run on future NT versions.
DriverX C Example
This example illustrates how a 16-bit application controlling an A/D converter card can be quickly and easily ported to 32-bit Windows using the DriverX C library.
Original 16-bit code
///////////////////////////////////////////////////////////////////////
// ackdata.c
//
// Acquires A/D converter values for one roll scan.
#include <stdio.h>
#include <dos.h>
#include <conio.h>
unsigned short g_buffer[1024];
int g_nCount=0;
/////////////////// CardIsr()
//
// Interrupt service routine for RADEXM-20. Called when analog-to
// digital conversion is complete. Stores value in global buffer.
void CardIsr()
{
// Read data
g_buffer[g_nCount++] =
inp(CARD_BASE+DATA_REG) + ((inp(CARD_BASE+DATA_REG+1))*256);
// Acknowledge the interrupt with the card
outp( CARD_BASE + STATUS_A, 0 );
// Reset PIC
outp(0xA0,0x20);
outp(0x20,0x20);
}
int main()
{
void interrupt (*prevVect)(_CPPARGS);
unsigned char ch;
// Hook up interrupt
prevVect = getvect( CARD_IRQ );
setvect( CARD_IRQ, CardIsr );
// Setup PIC
ch = inp( 0xA1 );
outp( 0xA1, ch&0xFA );
// Setup RADEXM-20 for
// interrupt on channel 0 acquisition
outp( CARD_BASE + STATUS_A,0x84 );
// Wait for acquisition done or ESC key...
32-bit code using DriverX library
Following is the previous 16-bit example ported to Windows NT and Windows 95 using the DriverX.
///////////////////////////////////////////////////////////////////////
// ackdata.cpp
//
// Acquires A/D converter values for one roll scan.
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <process.h>
#include "whc.h"
unsigned word g_buffer[1024];
int g_nCount=0;
// Define a couple macros mapping inp and outp to DriverX calls
HWPORT* g_pPort;
#define inp(x) HwInp(g_pPort,(x)-CARD_BASE)
#define outp(x,c) HwOutp(g_pPort,(x)-CARD_BASE,c)
/////////////////// CardIsr()
//
// Interrupt service routine for RADEXM-20. Called when analog-to-
// digital conversion is complete. Stores value in global buffer.
void CardIsr()
{
// Read data
g_buffer[g_nCount++] = inp(CARD_BASE+DATA_REG)+ ((inp(CARD_BASE+DATA_REG+1))*256);
// Acknowledge the interrupt with the card
outp( CARD_BASE + STATUS_A, 0 );
// No need to reset PIC (DriverX drivers manage this)
}
int main()
{
// Setup device context
HWDEVICE* pDevice = HwNewDevice( CardIsr );
if (!HwConnectDevice(pDevice,"Card0")) return 1;
// Store port context info for port I/O calls
g_pPort = HwDevicePorts(pDevice);
// Create thread to run ISR in the background
DWORD nThreadId;
HANDLE hThread = (HANDLE)_beginthreadex (NULL, 0, HwIsrThreadProc, pDevice, CREATE_SUSPENDED, &nThreadId);
// Enable the ISR
HwEnableIsr(pDevice, hThread, nThreadId);
// Setup RADEXM-20 for
// interrupt on channel 0 acquisition
outp( CARD_BASE + STATUS_A, 0x84 );
// Wait for acquisition done or ESC key...
Memory Mapping
Device DPRAM ranges can be easily accessed using the DriverX MapDeviceMemory routine. This routine sets up an alias virtual address range corresponding to the physical memory range for a hardware device. The device can then be accessed using normal pointer mechanisms.
Performance is very good since the memory is accessed directly without going through the DriverX driver.
Note that on NT, the maximum amount of device memory that can be mapped is 192MB. This is a limitation of the NT operating system, not DriverX. This 192MB can be further reduced by other drivers that may also be mapping hardware memory.
PCI Support
DriverX dramatically simplifies PCI device control. On NT, a PCI device can be configured in one easy step using the DriverX Device Configuration Utility or the DriverX ConfigureDevice API routine. For Windows 95, DriverX includes a standard INF file for configuring PCI devices through the Windows 95 Add New Hardware Wizard.
Using DriverX, developers can quickly begin work on controlling a PCI device without getting bogged down in the coding of complex PCI device initialization and Plug & Play event handling routines.
DriverX supports devices with up to 16 I/O regions and up to 16 memory mapped regions. Developers using DriverX can open a handle to a PCI device using the DriverX ConnectDevice routine and can then access any I/O or memory mapped region with no other configuration or resource assignment work required.
DriverX also supports multiple PCI devices of the same type and provides routines for writing and reading to/from the PCI configuration space of a device.
A DriverX PCI device configuration can request a contiguous buffer for PCI devices that support busmaster DMA. An application can program a device for a DMA transfer using a DMA buffer physical address returned from the DriverX GetPhysicalAddress routine. The DriverX MapDmapBuffer routine can be used to access a DMA buffer from an application using normal pointer mechanisms.
DriverX - ActiveX Usage
To use a Hardware ActiveX control, the developer simply drags an ActiveX control from the container's toolbar and drops it onto his or her main application form. During initialization, the control is initialized and associated with a registry device configuration-previously entered either by the application's setup program or through methods provided by the control.
Private Sub Form_Load()
' Initialize control
MyControl.Connect "MyConfiguration"
' Enable firing of interrupt events
MyControl.EnableInterrupt
End Sub
After initialization, the control can then be used to access the hardware device. For instance, in the following interrupt event handler for an A/D converter, the converted digital value is read and the interrupt is acknowledged.
Private Sub MyControl_Interrupt()
' Store converted value into global data array
Data(DataCount) = MyControl.inp(0)
DataCount = DataCount+1
' Acknowledge the interrupt by writing to
status register
MyControl.outp 0, &HFF
End Sub
DriverX Routines
The following routines are provided by the DriverX C library, C++ classes, Delphi unit, and ActiveX (OCX) control:
Port I/O Routines |
|
---|---|
AllocateOpQueue | Allocate an operation queue. |
AllocateOpQueue | Allocate an operation queue. |
AutoIncReadPort | Do a repeated read from a port into a buffer. |
AutoIncWritePort | Do a repeated write from a buffer to a port. |
DeleteOpQueue | Free an operation queue. |
Execute | Execute an operation queue. |
GetQueueBuffer | Retrieve data read from queue execution. |
GetQueueResults | Get the results from a queue execution. |
Inp | Do a byte, word, or DWORD port read. |
MapPorts | Set up a port range on the fly. |
Outp | Do a byte, word, or DWORD port write. |
QueueAndTemp | Modify a queue temporary variable. |
QueueBreakIfAnd | Queue command to break on a status bit. |
QueueBufInp | Queue a port read into a queue buffer. |
QueueBufOutp | Queue a port write from a queue buffer. |
QueueDelay | Queue a command to wait x microseconds. |
QueueInp | Queue a byte, word, or DWORD port read. |
QueueLoopback | Set up a loop within an operation queue. |
QueueOrTemp | Modify a queue temporary variable. |
QueueOutp | Queue a byte, word, or DWORD port write. |
QueueTempOutp | Queue command to write queue temp variable. |
QueueWhileInpAnd | Queue command to loop on a status bit. |
QueueWhileInpEqual | Queue command to loop on a status bit. |
UnmapPorts | Clean up a mapped port range. |
Memory Mapping Routines |
|
---|---|
GetMemoryPointer | Returns a virtual address for a memory range. |
GetPhysicalAddress | Returns the physical address of a memory range. |
MapDeviceMemory | Map a configured memory range for a device. |
MapDmaBuffer | Map the DMA buffer for a device. |
MapMemory | Map a specified physical memory range. |
Interrupt Handling Routines |
|
---|---|
BeginIsrSynch | Synchronize with an ISR thread. |
ConnectInterrupt | Hook a specified interrupt. |
DisableIsr | Disable interrupt handling from a device. |
DisconnectInterrupt | Unhook an interrupt. |
EnableIsr | Enable interrupt handling from a device. |
EndIsrSynch | End synchronization with an ISR thread. |
GetIsrData | Return register value for last ring-0 ISR operation. |
GetIsrIdData | Return register value for last ring-0 ISR ID operation. |
Configuration Routines |
|
---|---|
ConfigureDevice | Create a device configuration. |
ConfigureDriver | Configure the DriverX kernel-mode driver. |
DeleteDeviceConfig | Delete a device configuration. |
EnumerateDevices | Enumerate through all device configurations. |
GetDeviceConfig | Retrieve a device configuration. |
IsDeviceRunning | See if a device has been started. |
IsDriverConfigured | See if the DriverX driver is configured. |
StartDevice | Starts a device and allocates resources. |
StopDevice | Stops a device and frees resources. |
Miscellaneous |
|
---|---|
ConnectDevice | Open a handle to a device. |
DisconnectDevice | Close a handle to a device. |
GetBusDataByOffset | Read from PCI configuration space. |
IsOn95 | See if app is running on Windows 95/98. |
IsOnNT | See if app is running on Windows NT. |
SetBusDataByOffset | Write to PCI configuration space. |
SetErrorHandler | Set the error handler for critical DriverX errors. |
DriverX Technical Information
Interrupt handlers
Using DriverX, a developer can create an interrupt handler in his/her
application. The DriverX library manages an independent Win32 thread
which calls the developer's ISR in response to hardware interrupt
events. There are no restrictions on what can be called from the ISR.
The DriverX library provides a simple mechanism for avoiding
concurrency problems with data shared between the ISR and other
application code. DriverX is also designed to avoid any situations
where the main thread could be manipulating the device while the ISR
thread is handling an interrupt.
Driver-side repetitive operations
DriverX includes fast routines implemented completely on the driver
side to read and write buffers with common autoincrementing or
handshaking hardware.
Registry Support
DriverX provides support for registry storage of device
configurations-- including port base and count and interrupt level.
By using this option, the developer can take advantage of Windows
resource conflict detection and standardized configuration features.
Performance Optimizations
DriverX provides a number of features to enhance data throughput to and from hardware devices.
These features include fast auto-increment read/write routines and port operation queuing routines.
// Define the acquisition loop
HwQueueWhileInpAnd(q,
STATUS_REG_OFFSET,
1,
STATUS_BIT,
FALSE,
STATUS_TIMEOUT);
HwQueueBufInp(q, DATA_REG_OFFSET);
HwQueueLoopback(q);
// Execute the queued instructions with one call to driver
HwExecute(device,q);
The DriverX BreakIfAnd routine can also be used to break out of an acquisition loop on a particular register value.
The DriverX GetQueueResults routine can be used to determine how many bytes of data were transferred before a timeout or a break.
Licensing
DriverX includes a single-seat developer license. It also includes a license for unlimited redistribution of the DriverX kernel-mode drivers and libraries with applications. Redistribution is also permitted with custom controls and DLLs provided that the custom control or DLL is for a specific device and does not export the general purpose hardware control routines provided by DriverX.