---
# System prepended metadata

title: How to build device driver on Raspberry Pi?
tags: [Raspberry Pi]

---

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.

![elecrow-pitower-2](https://hackmd.io/_uploads/S1Jn_C2Sbl.jpg)

**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).