Title : Hardwire Interfacing under Linux
Author : Professor
.oO Phrack 50 Oo.
Volume Seven, Issue Fifty
11 of 16
H A R D W A R E I N T E R F A C I N G F O R T H E
L I N U X O P E R A T I N G S Y S T E M
By The Professor <professr@hackerz.org>
Computer control of real world devices has been an out of reach fantasy for
most people. In the past, it has rarely been seen outside the R&D labs of
hardware design companies, universities, and a few dedicated hobbyist's
basements. It takes not only a skilled programmer, but also a person that can
design and build small circuits.
In this article, I will show you how to use a standard IBM/PC parallel
printer port to control devices, such as bells, relays, and lights. I will
also show you how to take input from devices such as DTMF decoder IC's, analog
to digital converters, and switches.
To access the I/O port, the compiled program must be either executed by root
or be suid root. This could be a potential system security hazard so be
warned. In order to grant permissions to the port, one must use the function
ioperm().
Syntax (also see the man page):
#include <unistd.h>
ioperm(BASE_ADDRESS,NUM,PERMISSION_BIT);
The first parameter is the port number to set permissions of.
The second parameter is the number of consecutive ports to set permissions of.
(i.e. if num==3, BASE_ADDRESS, BASE_ADDRESS+1, and BASE_ADDRESS+2 are set).
The third parameter is 1 to give the program permissions or 0 to remove them.
Sending and receiving data via the port is done with the commands, inb() and
outb().
Syntax:
#include <asm/io.h>
value=inb(address); (address can be BASE_ADDRESS+1 or BASE_ADDRESS+2)
outb(value,BASE_ADDRESS);
O U T P U T
Making individual output data lines of a parallel printer port "turn on" is as
simple as selecting them with a corresponding binary value. Pin 2 (D0) is the
least significant bit and pin 9 (D7) is the most significant bit. If you
wanted bits 0, 2, 3, 4, and 6 to "turn on" or go high (+5v) while leaving 1,
5, and 7 low (ground) you would first convert the binary value to decimal and
then send that value to the port. (actually, there is no reason why you can't
just send the binary value to the port)
D7 D6 D5 D4 D3 D2 D1 D0
0 1 0 1 1 1 0 1 == 1011101 == 93
outb(93,BASE_ADDRESS);
If you want all lines low or "off", you send a 0.
If you want them all high or "on", you send 255.
Controlling the status of the individual bits of the I/O port is a simple
way of controlling solid state relays, optocouplers, LED's and so on. You
could very easily and very safely control a high wattage lighting system in
this manner. (assuming you are using solid state relays with back EMF
protection). This could/would be good for closet cultivators experimenting
with the horticulture of cannabis sativa or any other plant. Have you ever
wanted things such as lights and irrigation systems to come on or turn off at
certain times? That's what your crontab file is for! The possibilities are
endless.
I N P U T
Standard IBM/PC parallel printer ports have nine control lines capable of
inputting real world data. Each printer port has three address locations. The
base address is used to transmit data. The next address can input five data
bits, using pins 11, 10, 12, 13, and 15 (referred to as BASE_ADDRESS+1 I7
through I3), and the third port address can input or output a nibble of
information using pins 17, 16, 14, and 1 (referred to as BASE_ADDRESS+2 I3
through I0). The third port address pins must be set HIGH so we can read from
BASE_ADDRESS+2. I'll show you how in the example.
The inputs are all active LOW, meaning your device must short them to ground
to create a signal (switch, analog to digital converter, DTMF decoder, etc).
This is not a problem, as most devices already do this. The ones that don't,
just use an inverter.
The simplest method of inputting eight data bits is to read the high nibble
from the (BASE_ADDRESS+1) and the low nibble from the (BASE_ADDRESS+2). These
two nibbles can be logically ORed together to form a data byte. Some of the
data bits are hard-wired on the printer card for active HIGH operation. To
get around this, I use four sections of a 7404 hex inverter to re-invert the
inverted data lines.
I7 I6 I5 I4 I3 I2 I1 I0 BASE_ADDRESS+1 INPUT LINES
11 10 12 13 15 -- -- -- PIN NUMBER (-- = NOT USED)
I7 I6 I5 I4 I3 I2 I1 I0 BASE_ADDRESS+2 INPUT LINES
-- -- -- -- 17 16 14 1 PIN NUMBER (-- = NOT USED)
Notice both I3's of both ports are used. Pin 15 (ERROR) is the 9th input
of a standard IBM/PC parallel printer port. No offense to this pin, but it's
a pain in the ass to use and I only use it when I *have* to. Through
software, I disregard it.
Check out this example:
/* next line sets all open collector output pins HIGH
so we can read from BASE_ADDRESS+2) */
outb(inb(BASE_ADDRESS+2) || 15 , BASE_ADDRESS+2);
High_Nibble = inb(BASE_ADDRESS+1);
Low_Nibble = inb(BASE_ADDRESS+2);
High_Nibble = High_Nibble & 0xF0; /* 0xF0 = 11110000 */
Low_Nibble = Low_Nibble & 0x0F; /* 0x0F = 00001111 */
Data_Byte = High_Nibble | Low_Nibble;
Pretty simple, eh? This means you can use I7 through I4 in BASE_ADDRESS+1
and I3 through I0 in BASE_ADDRESS+2 to give you 8 bits of data input.
All of the data lines must use a pull up resistor. This includes the
hard-wired active HIGH pins *after* the 7404 inverter. This lets any device
produce both a high and low logic signal. Pull up resistors simply pull all
the data lines high so software sees all 0's unless you short a pin to ground.
(Remember these are all active LOW inputs -ground means 1)
Pins 14, 17, 1, and 11 are all hard-wired for active HIGH operation. These
are the pins that are signaled through the 7404 inverter IC (which makes them
just like the rest of the pins for ease of use).
NOTES:
*** When compiling programs using these routines, use the -O2 optimize flag,
or else you'll have some headaches.
Port 888 is the 1st parallel printer port (LPT1)
I am not responsible for your mistakes. If you plug 120vAC directly into
your parallel port, I guarantee you'll destroy your computer. Use optically
isolated solid state relays to switch high current.
For any more info regarding I/O port programming, schematics to some fun
projects, or to send a complaint, e-mail professr@hackerz.org
If you don't like my code, keep in mind that I design hardware for a living.
I am not a programmer, nor have I ever claimed to be one. My programs are
elegant on occasion, but mostly just get the job done without actually doing
it the best way.
If you want schematics showing how to hook up the 7404 to the port, mail me.
I have some interesting things there regarding circuit design. One of my
favorites is a software package called "PADS" Personal Automated Design
Software. It is a CAD package for schematics and PCBoard Design. The copy
on my web page is a public domain demo. This demo is fully functional in
every way. It only limits you to something like 20 IC's, 300 tie points, etc.
I usually do not go over these limits.
Maybe this article will replace the IO-Port [mini] How-To 'cause that is only
about 24 lines of text.
E X A M P L E S
A N D
D I A G R A M
/* simple program to send data via parallel port */
#include <unistd.h>
#include <asm/io.h>
#define BASE_ADDRESS 888 /* 1st Parallel Port */
main() {
int port_data=0;
int Data_Byte=255;
ioperm(BASE_ADDRESS,3,1); /* set permission on port */
outb(Data_Byte,BASE_ADDRESS);
printf("Sent 255 to port %d to turn all pins HIGH\n",BASE_ADDRESS);
ioperm(BASE_ADDRESS,3,0); /* take away port permission */
return(0);
}
/* end of simple program to send data via parallel port */
/****************************************************************************/
/* simple program to take in 8 bit input via parallel port */
#include <unistd.h>
#include <asm/io.h>
#define BASE_ADDRESS 888 /* 1st Parallel Port */
main() {
int port_data=0;
int High_Nibble, Low_Nibble, Data_Byte;
ioperm(BASE_ADDRESS,3,1); /* set permission on port */
outb(inb(BASE_ADDRESS+2) || 15 , BASE_ADDRESS+2);
High_Nibble = inb(BASE_ADDRESS+1);
Low_Nibble = inb(BASE_ADDRESS+2);
High_Nibble = High_Nibble & 0xF0; /* 0xF0 = 11110000 */
Low_Nibble = Low_Nibble & 0x0F; /* 0x0F = 00001111 */
Data_Byte = High_Nibble | Low_Nibble;
printf("LN=%d HN=%d DB=%d\n",Low_Nibble,High_Nibble,Data_Byte);
ioperm(BASE_ADDRESS,3,0); /* take away port permission */
return(0);
}
/* end of simple program to take in 8 bit input via parallel port */
/****************************************************************************/
I I I I I
0 6 7 5 4
P
A
_ P
S E S
T R E
R _ B | L
O A U E E
B D D D D D D D D C S N C
E 0 1 2 3 4 5 6 7 K Y D T
_____________________________________
1 (o o o o o o o o o o o o o) 13
14 \ o o o o o o o o o o o o/ 25
`---------------------------------'
_ _ | PINS 18 |
A E I S |<----THROUGH 25---->|
U R N E | GROUND |
T R I L
O O T |
| R I
F N
E P
E U
D * T ** ERROR LINE IS NOT USED AS I3
* (DISREGARDED VIA SOFTWARE)
I I I I
1 3 2 3
/******************** End of my little text file / how-to *******************/
EOF