Rutiner för att nå I/O-portar finns i/usr/include/asm/io.h
(eller linux/include/asm-i386/io.h
i 'the kernel source
distribution'). Rutinerna där är inline makron, så det räcker att göra
#include <asm/io.h>
, inga ytterligare bibliotek behövs.
Beroende på brister i gcc (åtminstone i 2.7.2.3 och lägre) och i egcs
(alla versioner), måste du kompilera källkod som använder dessa
rutiner med optimeringsflaggan på (gcc -O1
eller högre), eller alternativt
#define extern
till ingenting, (dvs. #define extern
på en i övrigt blank rad) innan du gör #include
<asm/io.h>
.
Om du vill avlusa, 'debug', kan du använda gcc -g -O
(åtminstone med
moderna versioner av gcc), även om optimeringen ibland gör att debuggern
beter sig lite underligt. Om detta besvärar dig, kan du lägga de rutiner
som anropar I/O-portarna i separata filer och kompilera endast dem med
optimeringsflaggan på.
Innan du anropar en port, måste du ge ditt program tillstånd till detta.
Detta gör man genom att anropa ioperm()
funktionen (som finns deklarerad
i unistd.h
, och definierad i 'kernel') någonstans i början av ditt program,
innan någon I/O-port anropas. Syntaxen är ioperm(from, num, turn_on)
,
där from
är den första portadressen som ska ges tillstånd och num
är
antalet konsekutiva adresser. Till exempel, ioperm(0x300, 5, 1)
ger
tillstånd till portarna 0x300 till 0x304 (totalt 5 portar). Det sista
argumentet är en bool som specificerar om du vill ge accesstillstånd
(true(1)) eller ta bort tillståndet (false(0)). Du kan använda
ioperm()
upprepade gånger för att ge tillstånd till ickekonsekutiva
portadresser. Se ioperm(2)
manualen.
ioperm()
anropet kräver att ditt program har rootprivilegier. Det krävs
att du antingen kör som root, eller gör setuid root.
Du kan släppa rootprivilegierna så snart du har anropat ioperm()
. Det är
inte nödvändigt att explicit släppa dina accessrättigheter med
ioperm(..., 0)
mot slutet av ditt program. Detta sker automatiskt när
processen avslutas.
Om du gör setuid()
till en non-root user förstörs inte de
accessrättigheter som är redan givna av ioperm()
, men fork()
förstör dem (child processen får inga rättigheter, men parent behåller
dem).
ioperm()
kan endast ge access rättigheter till portarna 0x000 - 0x3ff.
För att komma åt högre portadresser, kan man använda iopl()
, som ger
access till alla portar på en gång. Använd nivå 3 (dvs iopl(3)
)
för att ge ditt program tillgång till alla portar. (Men var försiktig - att
skriva på fel port kan orsaka allehanda otrevliga saker med din dator).
Du behöver rootprivilegier för att anropa iopl()
.
Se iopl(2)
manualen.
Sedan, för att komma åt portarna... För att läsa in en byte, (8 bitar)
från en port, call inb(port)
, den returnerar den byte den läser. För
att ställa ut, call outb(value,port)
(notera parameterordningen).
För att läsa in 16 bitar från port x
och x+1
,en byte från vardera,
call inw(x) och för att ställa ut, call outw(value,x)
. Är du osäker
på om du skall använda byte eller word instruktioner, är det troligen
inb()
och outb()
- flertalet apparater konstrueras för bytevis
portaccess. Notera att alla portaccesser tar åtminstone cirka en
mikrosekund att utföra.
För övrigt fungerar makroanropen inb_p()
, outb_p()
, inw_p()
,
och outw_p()
på samma sätt som ovannämnda, förutom att de lägger till ca en
mikrosekund efter varje portaccess. Du kan göra fördröjningen ännu
längre, ca 4 mikrosekunder, med #define REALLY_SLOW_IO
innan du gör
#include <asm/io.h>
. Dessa makron gör normalt (såvida du inte gör
#define SLOW_IO_BY_JUMPING
, vilket blir mindre noggrant) access
till port 0x80 för att skapa delay, så du behöver först ge accessrätt
till port 0x80 med ioperm()
. (Skrivning på port 0x80 påverkar ingenting).
För mer flexibla delay-metoder, läs vidare.
Det finns sidor till ioperm(2)
, iopl(2)
och ovannämnda makron i
någorlunda färska utgåvor av Linux manual.
/dev/port
Ett annat sätt att komma åt I/O-portar är open()
/dev/port
(en 'character device', major number 1, minor 4) för läsning och/eller
skrivning (stdio f*()
funktionerna har intern buffring, så använd inte
dem). Gör sedan lseek()
till den aktuella byten i filen (fil
position 0 = port 0x00, fil position 1 = 0x01, och så vidare), och
read()
eller write()
en byte eller ett ord till eller från den.
Naturligtvis behöver ditt program accessrättigheter till /dev/port
för att metoden skall fungera. Denna metod är sannolikt långsammare
än den normala metoden enligt ovan, men behöver varken optimeringsflaggan
vid kompilering eller ioperm()
. Det behövs inte heller 'root access',
bara du ger 'non-root user' eller 'group' access till /dev/port
- låt
vara att detta är dumt ur systemsäkerhetssynpunkt, eftersom det är
möjligt att skada systemet, kanske till och med vinna 'root access',
genom att använda /dev/port
för att komma åt hårddisk,
nätverkskort, etc. direkt.