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?
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