From Hello World to Real World - Part 6/6

Written by Alger Pike

Introduction
Although setting code up to use interrupts is an extra step in the coding process for a device driver, it is often worth the effort. By using interrupts, the application can do other useful things while waiting for the device to signal. The time scale of the wait can often be several milliseconds (ms). This is enough time to do some data processing or update the screen. There are several functions that are in the devhlp library, that make adding interrupt support relatively simple.

The first step is to set up the function that will handle the interrupt and associate it with a particular interrupt. This function, the Interrupt Service Routine (ISR), is called whenever an interrupt is generated by the device. Once the device has issued the interrupt, the processor stops what it is doing and executes the ISR. The only mandatory action of this function is to issue an end of interrupt statement. Besides this mandatory action, the ISR is free do whatever it needs to do when the interrupt occurs. Keep in mind however, that an interrupt routine that is too resource intensive can reduce system performance. For this reason, it is advisable to keep your ISR as short as possible. At this point we add a new file to our collection of device driver code. It is called ints.c, and this is where the interrupt handler is located. A typical interrupt handler looks like the following: ''Figure 1: Interrupt Handler: Notice the special calling convention. This function will be called anytime the system receives an interrupt on level 9.''

The calling convention for the function is important. The operating system is expecting the function to have a certain calling convention. We must make our interrupt handler comply with the preset standard. For details on the definition of the STRATEGY calling convention see devhelp.h.

The next step is to associate the above ISR with a particular interrupt level. My board will generate an interrupt on level 9 in response to a user programmed event. To set the board for the appropriate event I set up another IOCTL call. This IOCTL will also tell the operating system to call int_hand when an interrupt 9 occurs. This is done using the DevSetIRQ library function. The code to do all of this is as follows: ''Figure 2: This IOCTL call setups up the D/A card to use interrupts when its timer is done. It also associates int_hand with interrupt level 9.''

After associating int_hand with interrupt 9, the operating system will call int_hand any time the board generates an interrupt.

The first task of this interrupt handler is to issue the DevEOI call. This call tells the 8259 PIC that it can send another interrupt on this same channel. The rest of the handler contains the device specific code. With this handler, I need to be able to post an event semaphore back to the 32-bit application that is waiting on this semaphore. There are devhlp library functions that allow a device driver to use a 32-bit shared event semaphore. The only condition placed on these functions, is that they must be called from a task or strategy time thread. This means we cannot call these function directly from our interrupt handler. There is, however, a way for the interrupt handler to call a second thread which runs at task level. This is done using a context hook. The context hook runs as a separate thread and runs in the task context so it can call all the task devhlp functions.

The first step is to create the hook. This is done using the devhlp function DevCtxAllocate. The init section is a good place to create the context hook and associate it with the proper function. Like with interrupts this function must have a predefined calling convention. The following code sets up a context hook for use by the interrupt routine: ''Figure 3: Excerpt from init. This code allocates the context hook to be used by the interrupt handler.''

The context hook will be executed in response to a DevCtxArm. This is done in the interrupt handler since we want the hook to execute when an interrupt occurs. Since this thread will run in a task time context it can use the devhlp functions that deal with event semaphores. The context function posts an event semaphore back to the 32-bit application is waiting on it.

This semaphore must first be opened by the device driver so that the driver can use it. This is done by passing the handle of a shared event semaphore, created by the 32-bit application, to the driver by using an IOCTL. The semaphore is then opened by the driver using the DevEventOpen devhlp library function: ''Figure 9: Before you can use the event semaphore you must first register it with the device driver. This must be done before your device generates any interrupts.''

This handle can now be used as a normal event semaphore, to trigger back to the 32-bit application. In order to post the event and tell the caller to continue, issue a DevEventPost. This function requires a task time thread to execute properly. Therefore it belongs in the context hook routine; notice the calling convention required for the context hook: ''Figure 10: Code required for the context hook. The hook is able to call task time devhlp functions and can post the opened event semaphore to the 32-bit application.''

Now whenever an interrupt occurs, the ISR will be triggered. The ISR then triggers the context hook, which in turn signals the main application.

At this point we are now finished writing our device driver. It has all of the code that it needs to manage the device. You have now seen many of the things that are required for a data acquisition device driver. The driver is responsible for three major areas: 1) It must be an application manager for the device, 2) It handles the devices I/O calls, and 3) handles the devices interrupt needs. With these three goals met the user is now free to start interfacing with the ring 3 application and start taking data for his particular application. Now you should have all the tools you need to write a device driver for your own data acquisition card. For additional information I refer you to "Writing OS/2 2.1 Device Drivers in C, 2nd Edition" by Steve Mastrianni and also IBM's DDK which is now free and online at http://service.boulder.ibm.com/ddk/.