.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 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 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 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 #include #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 #include #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