How to Monitor Parallel Port Interrupts in User Space in Linux

16 September 2008 in hardware

For a 40+ year old technology, the parallel port can be a surprisingly useful device for interfacing your computer with external devices.

DISCLAIMER

A computer is not a cheap device and home-made electronics are not always reliable. I take no responsibility for damage caused by you sticking shit into your parallel port or anywhere else on your computer to see what happens. I also take no responsibility for any side-effects of the following code.

Motivation

Anyway, I’m guessing you want to install an interrupt handler for the parallel port, but aren’t a kernel hacker and don’t want to read a ton of documentation? Well, fortunately I had the same issue and was able to get something similar working. Unfortunately dealing with the parallel port in user space isn’t exactly the same as writing a kernel module, and there are a few important things to remember:

  • Code which accesses the parallel port must be run as root
  • Only one piece of code which uses this method may listen for interrupts at a time
  • The parallel port cannot be sharing an IRQ (although this shouldn’t be an issue since the parallel port usually has a dedicated IRQ anyway, number 5 or 7 most of the time)

The code

So without further ado, here is what I’ve got:

#include <linux/ppdev.h>
#include <linux/parport.h>
#include <stropts.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/select.h>

#define PP_DEV_NAME "/dev/parport0"

int main()
{
    // Get a file descriptor for the parallel port
    int ppfd;

    ppfd = open(PP_DEV_NAME, O_RDWR);
    if(ppfd == -1)
    {
        printf("Unable to open parallel port!\n");
        return 1;
    }

    // Have to claim the device
    if(ioctl(ppfd, PPCLAIM))
    {
        printf("Couldn't claim parallel port!\n");
        close(ppfd);
        return 1;
    }

    while(1)
    {
        // Set up the control lines for when an interrupt happens
        int int_count;
        int busy = PARPORT_STATUS_ACK | PARPORT_STATUS_ERROR;
        int acking = PARPORT_STATUS_ERROR;
        int ready = PARPORT_STATUS_ACK | PARPORT_STATUS_ERROR;
        char int_value;

        ioctl(ppfd, PPWCTLONIRQ, &busy);

        // Let ppdev know we're ready for interrupts
        ioctl(ppfd, PPWCONTROL, &ready);

        // Wait for an interrupt
        fd_set rfds;
        FD_ZERO(&rfds);
        FD_SET(ppfd, &rfds);
        if(select(ppfd + 1, &rfds, NULL, NULL, NULL))
        {
            printf("Received interrupt\n");
        }
        else
        {
            continue; // Caught a signal
        }

        // Fetch the associated data
        ioctl(ppfd, PPRDATA, &int_value);

        // Clear the interrupt
        ioctl(ppfd, PPCLRIRQ, &int_count);
        if(int_count > 1)
        {
            printf("Uh oh, missed %i interrupts!\n", int_count - 1);
        }

        // Acknowledge the interrupt
        ioctl(ppfd, PPWCONTROL, &acking);
        usleep(2);
        ioctl(ppfd, PPWCONTROL, &busy);
    }

    return 0;
}

As you can see, what you’re essentially doing is just opening the parallel port device like any old file and then calling select() to wait until an interrupt (or some other signal) happens. Here all I’m doing is writing a line to the console whenever an interrupt is received, but of course you can do anything (so long as the interrupt handler is fast enough to deal with your interrupt frequency). There are also a few ioctl() calls for claiming the port and clearing interrupts, but for the most part, the comments are self-explanatory and the code can just be copied into your project.

There is of course one fundamental issue with this code, and that is that it blocks until an interrupt is received. My suggestion, and the way I use this code, is to launch off another thread that runs this code and just sits dormant until an interrupt occurs. This seems to be relatively low overhead and pretty reliable. I’ve avoided giving the sample code in a threaded example so that it’s easy to extract and use in your own projects. Feel free to email me if you have a question or suggestion.

Triggering

In case you’re wondering how to trigger a parallel port interrupt, it’s triggered on the rising edge (or on some boards the falling edge) of a signal on pin 10, so just short pin 10 to ground (pins 18-25) and you should see an interrupt triggered (it may be triggered when you unshort it).