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.