Building a “device driver” on [Raspberry Pi](https://www.ampheo.com/c/raspberry-pi/raspberry-pi-boards) usually means writing a Linux kernel module (LKM) that talks to hardware (GPIO/I²C/SPI/USB/PCIe) and exposes a clean interface to user space (/dev/..., sysfs, netlink, etc.). On Raspberry Pi, Device Tree + overlays are also a big part of the workflow.

**1) Decide what you actually need**
Before you write kernel code, check if user-space is enough:
**Use user-space (recommended if possible)**
* GPIO via libgpiod
* I²C via /dev/i2c-*
* SPI via /dev/spidev*
* USB devices often work via existing drivers
If you can do it in user-space, you avoid kernel crashes and maintenance burden.
**Write a kernel driver when…**
* You need low latency / precise timing
* You need interrupt handling in kernel space
* You need a proper device node with custom ioctls
* You’re adding support for new hardware not covered by existing drivers
**2) Understand Raspberry Pi’s model: Device Tree first**
On Raspberry Pi, hardware is described by Device Tree (DT), and extra hardware is typically enabled via Device Tree Overlays configured in config.txt.
Examples (common):
Enable I²C / SPI using dtparams in config.txt
Your driver often becomes a platform driver that binds to a DT node using compatible = "...".
**3) Set up the build environment**
On Raspberry Pi OS, you generally build modules against the running kernel:
1. Check your kernel:
`uname -r`
2. Install build tools and headers:
```
sudo apt update
sudo apt install -y build-essential raspberrypi-kernel-headers
```
3. Confirm headers are available:
`ls /lib/modules/$(uname -r)/build`
If /build is missing, you’ll need to match kernel/headers correctly (this happens sometimes after kernel upgrades on certain distros/images).
**4) Pick a driver type (most common on Pi)**
**A) Character device driver (common for sensors/custom hardware)**
You create a /dev/mydev node and implement read/write/ioctl.
A quick way is a misc device, which avoids manually allocating a major number.
**B) I²C or SPI driver**
* Use Linux subsystems: i2c_driver or spi_driver
* Bind via Device Tree
* Expose data via:
* hwmon, iio, input subsystem, or a char device (depending on device class)
**C) GPIO / interrupt-based driver**
* Use gpiod_* APIs
* Request IRQ via DT and handle it in the driver
**5) Minimal “skeleton” approach (what you implement)**
A typical Pi platform driver looks like:
* probe() — runs when DT matches your compatible
* remove() — cleanup
* resource acquisition:
* memory registers (devm_ioremap_resource)
* GPIO (devm_gpiod_get)
* IRQ (platform_get_irq, devm_request_threaded_irq)
* expose user interface:
* misc char device (simple)
* or sysfs attributes
* or register into iio/hwmon
**6) Build, load, and test (workflow)**
**Build (typical out-of-tree module)**
In your module directory:
`make -C /lib/modules/$(uname -r)/build M=$PWD modules`
**Load/unload**
```
sudo insmod mydriver.ko
dmesg | tail -n 50
sudo rmmod mydriver
```
**Make it load at boot**
* install to /lib/modules/.../extra/
* run depmod
* add to /etc/modules-load.d/*.conf
* or package with DKMS if you want it to survive kernel updates (common for long-term deployments)
**7) Device Tree overlay: how your driver “attaches” on Pi**
You typically:
1. Write an overlay .dts with:
* a node for your hardware
* compatible = "yourvendor,yourdevice"
* GPIO/I²C/SPI parameters
2. Compile to .dtbo
3. Enable in /boot/config.txt via dtoverlay=...
**8) Debugging tips that save hours**
* Use dmesg -w while inserting the module
* Check whether your device is bound:
* ls /sys/bus/platform/drivers/...
* ls /sys/bus/i2c/drivers/...
* ls /sys/bus/spi/drivers/...
* Confirm overlays loaded and pins assigned (Pi DT/overlay tooling is central here).