Determine IO port addresses via /proc/ioports

Since I picked up programming again (pSam and Parilux) I needed to know how to get to the IO address ranges of hardware ports. Under DOS you simply inspected part of System RAM at 0000:0400h. In Linux it is even more easy: just do a 'cat /proc/ioports' and you have all info in reach.
This section describes how to get to that information and shows a first example of how it's done. This is an evolving page, so check back once in a while since the content is liable to change in time. Do not rely on Google: they censor my pages since I put the word 't e r r ror ist' (no spaces) 32 times on one webpage and that makes ME a T-thingy as well.

The /proc filesystem

The /proc filesystems is special. It has a list of files in it, which are built up by the Linux system at boot time and when major changes have taken place. Below is the list of files in my /proc FS:

jan@beryllium:~$ ls /proc
295   3231  3315  3347  3550  3574  3591  cmdline    filesystems  kmsg       partitions   tty
3     3235  3316  3348  3552  3576  3598  cpuinfo    fs           loadavg    scsi         uptime
3125  3256  3317  3349  3555  3577  3603  crypto     ide          locks      self         version
3128  3263  3318  3427  3563  3579  3667  devices    interrupts   meminfo    slabinfo     vmstat
3139  3287  3319  3470  3565  3583  3686  diskstats  iomem        misc       stat
3144  3291  3330  348   3566  3584  3690  acpi       dma          ioports    modules      swaps
3162  3304  3343  3498  3568  3586  37    asound     driver       irq        mounts       sys
3221  3307  3345  3526  3569  3587  4     buddyinfo  execdomains  kallsyms   mtrr         sysrq-trigger
3226  3314  3346  3547  3572  3588  47    bus        fb           kcore      net          sysvipc
jan@beryllium:~$
   
To prevent horizontal scroll bars I removed some of the more obscure devices.

You can inspect the data in the /proc filesystem with your cat:

jan@beryllium:~$ cat /proc/uptime
1576.52 1539.09
jan@beryllium:~$ cat /proc/filesystems
nodev   sysfs
nodev   rootfs
nodev   bdev
nodev   proc
nodev   sockfs
nodev   futexfs
nodev   tmpfs
nodev   pipefs
nodev   eventpollfs
nodev   devpts
        cramfs
nodev   ramfs
nodev   devfs
nodev   mqueue
	ext3
	ext2
	reiserfs
nodev   usbfs
nodev   usbdevfs
jan@beryllium:~$
   
I can recommend to try some of these devices just to satisfy your curiosity. Especially /proc/cpuinfo can tell you if your salesguy cheated with the CPU of your new machine.

procs.mod : the source

Below is the source of the first attempt to process the /proc/ioports device. It specifically looks for the IO addresses for the LPT ports (called 'parport' in Linux). I had to make GetHex since this is not part of the 'TextIO' library. I had to make GetString since I needed to read until end of line instead of the first whitespace token.

MODULE procs;

IMPORT  Strings, InOut, TextIO;

VAR	InFile			: TextIO.File;
	name			: ARRAY [0..63] OF CHAR;
	port0, port9		: CARDINAL;
	ch     			: CHAR;


PROCEDURE GetHex (VAR  num    : CARDINAL);

VAR	  chr	 : CHAR;
	  pos	 : CARDINAL;

BEGIN
   num := 0;
   LOOP
      TextIO.GetChar (InFile, chr);
      chr := CAP (chr);
      IF  (chr < '0')  OR  (chr > 'F')  OR  ( (chr > '9')  AND  (chr < 'A') )  THEN  EXIT  END;
      IF  chr > '9'  THEN
         pos := ORD (chr) - ORD ('A') + 10
      ELSE 
         pos := ORD (chr) - ORD ('0')
      END;
      num := num * 16 + pos
   END;
   TextIO.UndoGetChar (InFile)
END GetHex;


PROCEDURE GetString ( VAR  str		: ARRAY OF CHAR);

VAR   i	  	    : CARDINAL;
      chr	    : CHAR;

BEGIN
   i := 0;
   REPEAT
      TextIO.GetChar (InFile, chr)
   UNTIL  chr > ' ';
   LOOP
      IF  chr = 12C  THEN  EXIT  END;
      IF  i <= HIGH (str)  THEN  str [i] := chr  END;
      INC (i);
      TextIO.GetChar (InFile, chr)
   END;
   IF  i <= HIGH (str)  THEN  str [i] := 0C  END
END GetString;


BEGIN
   TextIO.OpenInput (InFile, "/proc/ioports");
   IF  TextIO.Done () = FALSE  THEN
      InOut.WriteString ("Cannot access '/proc/ioports'. Aborting.");
      InOut.WriteLn
   ELSE
      InOut.WriteString ("Opened '/proc/ioports' for reading'. Commencing.");
      InOut.WriteLn
   END;
   LOOP
      IF  TextIO.EOF (InFile) = TRUE  THEN  EXIT  END;
      GetHex (port0);
      TextIO.GetChar (InFile, ch);
      GetHex (port9);
      REPEAT
         TextIO.GetChar (InFile, ch)
      UNTIL  ch = ':';
      GetString (name);
      IF  Strings.pos ('parport', name) = 0  THEN
         InOut.WriteString ("Parport device found. Ports assigned from ");
	 InOut.WriteHex (port0, 4);
	 InOut.WriteString (' - ');
	 InOut.WriteHex (port9, 4);
	 InOut.WriteString (" for device ");
	 InOut.WriteString (name);
      	 InOut.WriteLn
      END
   END;
   InOut.WriteLn
END procs.
   

Houston, we have an error.

Although the program ran flwalessly for some time, today it malfunctioned. I put a new PCI LPT port in Beryllium. And when I ran the procs executable, the parport1 device was found, but it was assigned IO address range [0..0] which of course is not very sensible.

The error had to be in the GetHex function. Inspection of the /procs/ioports file gave a clue: there was whitespace in front of the hex numbers. And my GetHex did not remove the whitespace. So I changed the routine as below and now it runs fine. My new parport1 device is assigned the range [EFF0 .. EFF2] by Linux.

PROCEDURE GetHex (VAR  num    : CARDINAL);

VAR	  chr	 : CHAR;
	  pos	 : CARDINAL;

BEGIN
   num := 0;
   LOOP
      TextIO.GetChar (InFile, chr);
      IF  chr > ' '  THEN  EXIT  END
   END;
   LOOP
      chr := CAP (chr);
      IF  (chr < '0')  OR  (chr > 'F')  OR  ( (chr > '9')  AND  (chr < 'A') )  THEN  EXIT  END;
      IF  chr > '9'  THEN
         pos := ORD (chr) - ORD ('A') + 10
      ELSE 
         pos := ORD (chr) - ORD ('0')
      END;
      num := num * 16 + pos;
      TextIO.GetChar (InFile, chr)
   END;
   TextIO.UndoGetChar (InFile)
END GetHex;
   
The whitespace skipper should have been there from the beginning but I was too self assured. Now it is in and now it runs fine.

Page created 15 June 2007,