Parapin has two personalities: one for plain user-space operation (i.e., linking Parapin with a regular C program), and one for use with Linux kernel modules. The two personalities differ in how they are compiled (Section 4) and initialized (Section 5). Also, only the kernel version is capable of servicing parallel-port interrupts that are generated from Pin 10. Otherwise, the two personalities are the same.
The user-space library version of Parapin works very much like any other C library. However, kernel programming differs from user-space programming in many important ways; these details are far beyond the scope of this document. The best reference I've ever found is Alessandro Rubini's fantastic book, Linux Device Drivers. Go buy it from your favorite technical bookstore; or, if you don't have a favorite, use my favorite: BookPool. If you don't want to buy the book, a far inferior alternative (but better than nothing) is the free Linux Kernel Hacker's Guide.
In Parapin, there are five basic operations possible:
Each of these functions will be discussed in later sections.
Most of the functions take a pins argument--a single integer that is actually a bitmap representing the set of pins that are being changed or queried. Parapin's header file, parapin.h, defines a number of constants of the form ``LP_PINnn'' that applications should use to specify pins. The nn represents the pin number, ranging from 01 to 17. For example, the command
set_pin(LP_PIN05);turns on pin number 5 (assuming that pin is configured as an output pin; the exact semantics of set_pin are discussed in later sections). C's logical-or operator can be used to combine multiple pins into a single argument, so
set_pin(LP_PIN02 | LP_PIN14 | LP_PIN17);turns on pins 2, 14, and 17.
Usually, it is most convenient to use #define statements to give pins logical names that have meaning in the context of your application. The documentation of most ICs and other electronics gives names to I/O pins; using those names makes the most sense. For example, a National Semiconductor ADC0831 Analog-to-Digital converter has four I/O pins called VCC (the supply voltage), CS (chip select), CLK (clock), and D0 (data output 0). A fragment of code to control this chip might look something like this:
#include "parapin.h" #define VCC LP_PIN02 #define CS LP_PIN03 #define CLK LP_PIN04 #define D0 LP_PIN10 /* input pin */ ... clear_pin(CS); /* pull Chip Select low, tell it to acquire */ ... set_pin(CLK); /* clock it */ clear_pin(CLK);
This method has a number of advantages. First, it makes the code more readable to someone who has the ADC0831 documentation in hand. Second, the #define statements summarize the mappings of IC pins to parallel port pins, making it easier to build the physical interface. Also, if the physical interface changes, the #defines make it easy to remap parallel port pins to new IC pins.
Parapin's header file also provides these constants in an array called LP_PIN. This array is useful in some contexts where the pin number is being specified with a variable instead of a constant. For example:
/* runway lights: light up pins 1 through 9 in sequence */ while (1) { for (i = 1; i <= 9; i++) { set_pin(LP_PIN[i]); usleep(200); clear_pin(LP_PIN[i]); } }
Code such as the above fragment would be much more complex if the
programmer were forced to use constants. There is a direct
correspondence between indices in the array and pin numbers;
accordingly, only indices from 1 to 17 should be used. Programs
should never reference LP_PIN[0]
or LP_PIN[i]
where i > 17.