To read from and write to I/O ports, you must use the appropriate sized function:
unsigned inb(unsigned port) - read one byte from an 8-bit port
void outb(unsigned char byte, unsigned port) - write one byte to an 8-bit port
unsigned inw(unsigned port) - read one word from a 16-bit port
void outw(unsigned port) - write one word to a 16-bit port
unsigned inl(unsigned port) - read one long from a 32-bit port
unsigned outl(unsigned port) - write one long to a 32-bit port
No 64 bit port operations are defined, 64-bit architectures use a maximum of 32-bit operations on I/O ports
Manipulating I/O Ports in User Space
inb(/w/l) and outb(/w/l) can be used in user-space as they’re defined in <sys/io.h>, to use them a program must be run as root, compiled with “-O” to force extension of inline functions, and acquire permissions using:
ioperm - to get permissions for individual ports
iopl - to get permissions for the entire I/O space
Alternatively, user programs can access I/O ports using their device files in /dev/<device name>
String Operations on Ports
I/O ports can be read from and written to using C-strings using the following functions:
void insb(unsigned port, void *addr, unsigned long count);
void outsb(unsigned port, void *addr, unsigned long count);
void insw(unsigned port, void *addr, unsigned long count);
void outsw(unsigned port, void *addr, unsigned long count);
void insl(unsigned port, void *addr, unsigned long count);
void outsl(unsigned port, void *addr, unsigned long count);
When using string operations, look out for mismatches between byte-ordering rules of the host system and the I/O device (little endian vs big endian)
I/O Pausing
If the device is slower than your processor, it can be overclocked and miss data, to solve for this, use pausing I/O functions which implement a wait in between I/O operations.
Platform Dependencies
I/O port operations are highly platform dependent
For example, a port can be unsigned short in some architectures and unsigned long in others
Using I/O Memory
A popular alternative to I/O ports is I/O memory
I/O memory is RAM regions that a device makes available to the processor through the bus
Technically, it’s possible to access I/O memory using pointers, much like regular memory, but this is highly discouraged for security and stability concerns
I/O Memory Allocation and Mapping
I/O memory regions must be allocated prior to use, using:
struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);
This allocated a <len> long memory region starting at <start> for device <name>
When you’re done with a memory region, release it using:
void release_mem_region(unsigned long start, unsigned long len);
Accessing I/O Memory
To access I/O memory regions, one must obtain their address using:
void *ioremap(unsigned long phys_addr, unsigned long size);
void *ioremap_nocache(unsigned long phys_addr, unsigned long size);
The nocache version of ioremap gives a pointer to a read/write interface with no data caching, which drivers may sometimes need depending on the device
Read/Write I/O Memory
To read from and write to I/O memory, use the functions:
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);
void iowrite8(u8 value, void *addr);
void iowrite16(u16 value, void *addr);
void iowrite32(u32 value, void *addr);
Where addr is the address obtained from ioremap.
There are also versions of all of these functions that allow repeating read/write that look like:
void ioread8_rep(void *addr, void *buf, unsigned long count);
Note: count is expressed as number of relevant data units (8-bit/16-bit/32-bit)
Read/Write I/O Memory
You can also read from and write to I/O memory as a block using:
Fill a memory block with a repeating byte <value>
void memset_io(void *addr, u8 value, unsigned int count);
Copy from I/O memory <source> to regular memory <dest> <count> bytes
void memcpy_fromio(void *dest, void *source, unsigned int count);
Copy from regular memory <source> to I/O memory <dest> <count> bytes
void memcpy_toio(void *dest, void *source, unsigned int count);
Read/Write I/O Memory
There are also old functions discouraged from use to operate on I/O memory: