Search code examples
linuxlinux-device-driverkernel-moduledevice-driver

Accessing pins of Linux hardware inside device driver


I read this excellent DIY article on writing Linux device drivers, but am still not seeing the "forest through the trees" on a major item here. Ultimately the end user software (in user space) needs to communicate with the hardware (that the device drivers are driving/wrapping/adapting). Hardware is driven by electricity, and hence software commands need to be translated into hi/lo signals (1s and 0s) that get pushed out onto a circuit and into the connected hardware. A stupid simple example:

# Send a connected LED device a command to turn on at the software layer:
led.turnOn();

# In the device driver, somehow, translate this to 0x01 (1, or 00000001):
void turnOn() {
    int signal = 1;

    # Now, *somehow*, push this to the hardware with the following pinout (see below):
}

# Pinout
0   ---------------->  /----------\
0   ---------------->  |          |
0   ---------------->  |          |
0   ---------------->  | Hardware |
0   ---------------->  |          |
0   ---------------->  |          |
0   ---------------->  |          |
1   ---------------->  \----------/

What I'm not seeing is: in the device driver C code, how do I read/write bytes/data to and from the underlying hardware device?

The only theory I could see is that perhaps, because Linux devices are seen as "files" (dev/led) to the user space, perhaps writing data, such as 0x01, to dev/led is how we send commands to connected devices; and perhaps reading data from devices is how we read data off of them.

Am I heading in the right direction here, or way off track?


Solution

  • It really depends on the nature of the device and how it is connected to the system - it could be memory-mapped, or mapped to some kind of addressable I/O space, or on a bus such as PCI-e or USB, for example. The whole point is to abstract this in the driver so that the programmer doesn't have to care about the low-level details.

    For a PCI device for example the board might be mapped to a physical address range. Within this range you might access certain registers to control the hardware. Suppose you have a simple I/O card with a single 32 bit register and the card is mapped to physical address 0xf0000000. The register is used to control 32 LED outputs and you want to turn on LED 0:

    volatile uint32_t * const my_card_register = (uint32_t *) 0xf0000000;
                                          // address of PCI card register
    *my_card_register |= 0x00000001;      // set bit 0 in register to turn on LED