A vxWorks Filter Driver
Microsoft Windows device driver programmers are familiar with filter drivers. In fact, from Windows 2000 on, since no driver is ever the lowest level driver; all drivers can be thought of as filter drivers. Here we will see that it is possible to use this same technique to add functionality to vxWorks drivers.
A filter driver is layered on the top of the driver stack to provide feature extension or added functionality to the drivers below it. Encryption on top of a character device such as a modem can be implemented with a filter driver.
In our example, we will look at adding a filter driver on to the vxWorks filesystem.
First let’s look at the vxWorks driver stack layout before the filter driver is added.

The driver initialization process is as follows:
1. The block driver is initialized and returns a pointer to a BLK_DEV structure.
2. The Filesystem driver is initialized by passing the BLK_DEV pointer to it. It returns a pointer to a VOL_DESC structure.
The BLK_DEV structure contains a table of pointers that the Filesystem driver will use to perform operations on the Block Device.
The VOL_DESC structure contains a table of pointers that the I/O Subsystem will use to perform operations on Volume (File level operations).
The I/O Subsystem routes calls (open(), read(), close()) to the Filesystem driver, which in turn uses the calls provided by the Block Driver.
The BLK_DEV structure looks like this:
typedef struct /* BLK_DEV */
{
FUNCPTR bd_blkRd; /* function to read
blocks */
FUNCPTR bd_blkWrt; /* function to write
blocks */
FUNCPTR bd_ioctl; /* function to
ioctl device */
FUNCPTR bd_reset; /* function to
reset device */
FUNCPTR bd_statusChk; /* function to check
status */
●
● ● /* Device
Description Variables */
} BLK_DEV;
So, if the Filesystem needs to read a block, it calls the
function pointed to by the bd_blkRd pointer in the BLK_DEV structure, passing
it the Block Number, Size and a pointer to the data.
The I/O Subsystem uses the same method to handle file operations.
The VOL_DESC structure looks like this:
typedef struct /* VOL_DESC */
{
FUNCPTR de_create; /* function to
create a file */
FUNCPTR de_delete;
/* function to
delete a file */
FUNCPTR de_open;
/* function
to open a file */
FUNCPTR de_close;
/* function
to close a file */
FUNCPTR de_read;
/* function
to read from a file */
FUNCPTR de_write;
/* function
to write to a file */
FUNCPTR de_ioctl; /*
function to ioctl volume */
● ●
● /*
volume description variables */
} VOL_DESC;
The Filesystem announces itself to the Operating System’s I/O Subsystem with a call to iosDevAdd().
iosDevAdd (&pVolDesc,
devName, FsDrvNum);
If devName is “root”, then when you call open(“root/x”), the I/O Subsystem will call the de_open function pointed to in the Volume Descriptor.
Now we come to the pointer where we want to create a driver that is above the filesystem driver and will do some preprocessing or other work, before the filesystem ever sees the call.
What we need to do is create a driver with the same VOL_DESC interface that the filesystem uses.
Then we will associate that driver with the Volume instead
of the actual Filesystem driver so that it will receive the calls from the I/O
Subsystem.
What we will end up with is a system that looks like this:

The calls that set this up will look like this:
pBlkDev = blockDriverInit();
pvolDesc1 = fileSystemDriverInit(pBlkDev);
pvolDesc2 = filterDriverInit(pvolDesc1);
The call to iosDevAdd() will reside in the Filter Driver’s initialization routine and not in the File Systems Initialization routine. Microsoft Windows automatically takes care of the routing calls to the top driver in the driver stack. In vxWorks, this needs to be done by the programmer, so it is evident this technique can only be applied to a filesystem where the source is available.
I have used some simplifications, for instance, the volume descriptor actually contains a pointer to a structure that consists the function pointers, but generally this gives an idea of how to implement a Filter Driver under vxWorks.