...making Linux just a little more fun!

<-- prev | next -->

Linux: A Clear Winner for Hardware I/O

By P. J. Radcliffe


Hardware I/O is important to quite a few people but the traditional way in which Windows and Linux treat I/O is far from ideal. In this article we will look at these traditional ways, and show a new way which makes Linux a clear winner over Windows for hardware I/O.

Who cares about hardware I/O, where a computer directly controls hardware?

Problems With DOS, Windows, and Linux

DOS, Windows 95 and Windows 98 did not block I/O access in any way so command line or GUI programs could happily control I/O. This made I/O access easy but created both a vulnerability and a security hole. A misbehaving application could accidentally access any I/O address and so create havoc with network cards or hard disks. A malicious application could deliberately access networks or disks, and possibly wipe the entire hard disk.

Even if these problems are acceptable to you there is worse - none of these operating systems are supported by Microsoft and most of the newer applications and development tools do not run on them. Few people want to run such old systems so anything you create most probably wont be used by anyone else.

Windows 2000, NT, ME and XP all block I/O access by using security features build into the x86 microprocessor core. You would think this meant no I/O access is possible but - yes, there is a hack. For example, two programs called giveio.sys and totalio.sys can be used to bypass I/O security; you can download giveio.sys to suit Windows XP from http://www.physik.rwth-aachen.de/group/IIIphys/CMS/tracker/en/silicon/arcs_nt.html.

Such programs have several problems. Firstly, most of them give I/O access to every port, or access to a limited range of I/O for every application. Perhaps more important is the fact that these programs are hacks, and the very next Microsoft security update may render them useless. In fact, the existence of these I/O programs represents a security weakness for Windows so Microsoft should be finding ways to block their action.

Traditional Linux I/O restricts I/O access to the root user, or to device drivers which run in kernel space. For applications running under root privilege, the C code function ioperm() can give access a limited range of I/O ports between 0 and 0x3FF. The C code function iopl() can give access to all I/O ports (0 to 0xFFFF) and allow the application to turn interrupts on and off. See the UNIX info pages on both these functions for more details.

On close inspection none of these methods are really acceptable. It is very foolish to work as root during software development as a simple mistake can delete key parts of the operating system and so make your system crash and burn. Running an application with root privileges opens up all the vulnerability and security problems found with DOS and Windows 98. Writing kernel drivers is no better as they have the same security weaknesses and are significantly more difficult to write and debug compared to normal applications.

On the face of it, it seems as if Linux is little better than Windows for hardware I/O. It has the same weaknesses but at least I/O access will not disappear with the next security update.

The Linux Solution

Linux allows significant manipulation of the permissions and ownership of running software processes. It is possible to link together several well defined Linux features to create a bullet-proof method that will allow a user-level program to have I/O access to a limited range of I/O, typically just a parallel printer port or serial port. This means no security weaknesses and very easy programming of hardware I/O.

The tricks used to make this happen are as follows -

Running an application becomes quite simple -

   ./IO_enabler ./application_program  parameter_1  parameter_2 ...... 

The code behind the I/O enabling program is surprisingly short -

   /*--- optionally set process priority, 0= normal, -20 highest, +20 lowest.*/
     setpriority( PRIO_PROCESS, -10, 0) ;

   /*--- get access to the ports.*/ 
     if (ioperm(0x378, 3, 1)) 
       perror("Failed ioperm lp0 on") ;
   /*---  remove root privileges.*/     
     setgid( getgid() ) ;
     setuid( getuid() ) ; 

   /*--- overwrite code space with the application program, 
         send arguments to the application program.*/
     execvp(argv[1], &argv[1]) ;

   /*--- if get here exec must have failed.*/ 
     perror(" execv failed") ;

[ Note: in general, creating SUID programs without understanding all the security implications of doing so can be very dangerous. Do try this at home... but read much and consider deeply before you do. -- Ben ]

Useful Code Examples

Here are a few real bits of code to get started. Unless otherwise stated, compile a program ( lets call it 'x') as follows -

   gcc  -o  x  x.c

lp_tty_start.c: this I/O enabler allows access to the standard three printer ports and four serial ports but nothing else. Ensure it is owned by root with the UID bit set as described earlier in this article.

port_write_then_read.c: this will write a single byte to an I/O port and read back the new value of the I/O port. It is probably best to use /dev/lp0 data port (888) to test this program. To ensure that your /dev/lp0 is active, reboot your machine and go into the BIOS setup. Ensure the parallel port is in either "compatible", "SPP", or "bidirectional" mode. Remember to save your BIOS settings on exiting the setup screens. Start the program as follows to write 85 (55 hex or 01010101 binary) to the /dev/lp0 data port which will come out on pins 2 to 9 -

   ./lp_tty_start  ./port_read_then_write  888  85

iopl_start.c is another I/O enabler based around the iopl() function. This gives access to all I/O ports (0 to 0xFFFF) and has the ability to enable and disable interrupts. Like lp_tty_start it must be owned by root and have the SUID bit set.

blockio.c must be started with iopl_start. It will turn off interrupts and hang your machine for about 8 seconds, as well as causing pin D0 of /dev/lp0 to output a 500 kHz squarewave. After 8 seconds the program will terminate normally and your machine will resume its usual operations.

Start this as follows -

   ./iopl_start ./blockio

hide_lp_tty_start is a script file that shows how to hide the fact that your program was started using lp_tty_start. Unfortunately programs started thus way must take a fixed set of parameters. The application is simply a read of the data port of lp0 using port_read.c. Start it as follows -


What ports have you got? The code below implements a program that will check which serial and parallel ports are active and available on your machine. To make the program easy to run, the code is designed to be executed as root (not really a security issue, as you have the source and can see the code is benign).

main.c shows the basic flow of the code, and the instructions for use.

pp_access.h and pp_access.c form a neatly packaged parallel port access module that can save and restore the parallel port state, and help manipulate the bits in the port. You may want to reuse this module in your own applications.

serial_access.h and serial_access.c provide a similar functionality for the serial port.

make_pp_serial_check is a script file that compiles the code above to make an executable. Compile the code using make_pp_serial_check and execute the program (./pp_serial_check) as root with no parameters to see the instructions. The program can identify whether ports exist and perform a simple loopback test as further verification that the port works.

Some Dangers, and Help Needed!

Remaining dangers: the software dangers of using I/O have been largely eliminated by using the I/O enabling programs shown in this article but one danger does remain - if you fail to design the hardware circuitry properly, or mis-wire it, then you can blow up your precious parallel port (serial ports carry only signals, and are therefore much harder to damage.) Fortunately, there are a few simple warnings and a few simple circuits that can protect your ports. If there is enough response to this article then perhaps we can follow up with an article on safe hardware interfacing.

Where to now? This article has shown how user-level programs can safely access I/O in a very simple manner. Linux should now be the operating system of choice for all hardware I/O. It should be the operating system of choice for engineering students, computer science students, hobbyists and industry that uses hardware I/O.

Linux can access not only the standard ports such as the printer port; it can also connect to many of the plug-in cards that provide more sophisticated functions, such as relay cards and high speed analogue I/O.

What is needed is a wider range of I/O projects visible on the web so students, hobbyists, and practicing engineers have a head start when they create projects. New projects are needed and a lot of those old DOS projects need to be updated and ported to Linux. Can you be part of the Open Source community and help?



PJ Radcliffe is a senior lecturer at RMIT University in Melbourne Australia. His career started as an electronics/microprocessor engineer at Ericsson followed by consulting work in hardware and software, then an academic position at RMIT. Teaching has become a great pleasure, especially when linked with technologies and issues relevant to the workplace. In 2004 he received an award for "Student Centred Learning" from RMIT.

For many years he was a Microsoft junkie - but then had to run a lecture series on Linux, and got hooked. Who wouldn't be? Linux can be used as a turn-key GUI like Windows, a powerful server, and to control hardware.

His interests apart from Linux, software and hardware are... ( I'll remember in a tick)... (context switch)... a lovely wife who hates computers (the other women in my life, you see), three really nice kids, and a rather large garden.

Copyright © 2005, P. J. Radcliffe. Released under the Open Publication license unless otherwise noted in the body of the article. Linux Gazette is not produced, sponsored, or endorsed by its prior host, SSC, Inc.

Published in Issue 112 of Linux Gazette, March 2005

<-- prev | next -->