Skip to content

Instantly share code, notes, and snippets.

@0xff07
Last active October 13, 2022 06:33
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save 0xff07/d286f45649a7e05c32c4523631bd15e0 to your computer and use it in GitHub Desktop.
Save 0xff07/d286f45649a7e05c32c4523631bd15e0 to your computer and use it in GitHub Desktop.
An Example of Linux Kernel Module using GPIO Subsystem

An Example of Linux Kernel Module using GPIO Subsystem

This is a kernel module, with DT Overlay, to illustrate usage of GPIO subsystem in Linux kernel.

Hardware Envoronment

  1. Raspberry Pi 3 Model B Rev 1.2
  2. Arduino UNO

Software Environment

Raspberry Pi OS (32-bit) Lite (2020-08-20 release)

Hardware Configuration

Connect GPIO17 on Raspberry Pi Model B, through proper logic level shifter, to pin 7 on Arduino UNO.

Device Tree Overlay

First compile device tree:

dtc -@ -I dts -O dtb -o arduino-gpio.dtbo arduino-gpio.dts

Then move it to /boot/overlays/ folder:

sudo cp arduino-gpio.dtbo /boot/overlays/

After that, add the following lines to /boot/config.txt

dtoverlay=arduino-gpio

Reboot your Raspberry Pi. You may check current device tree after reboot with:

dtc -I fs /proc/device-tree | less

If everything goes well, you should be able to find somethings like this under gpio@7e200000 nodes:

...
gpio@7e200000 {
      ...
			arduino_gpio_conf {
				brcm,pins = < 0x11 >;
				phandle = < 0x86 >;
				brcm,pull = < 0x01 >;
				brcm,function = < 0x01 >;
			};
      ...
...

Also, below root node:

/{
        ...
        arduino_gpio {
                gpios = < 0x10 0x11 0x00 >;
                compatible = "arduino";
                status = "ok";
                phandle = < 0x87 >;
                pinctrl-0 = < 0x86 >;
                pinctrl-names = "default";
        };
        ...

Install

To build the module, simply use make:

make

To check if the module works, connect Arduino UNO with USB, then upload gpio_read.ino to Arduino UNO. Open serial console before install module.

sudo insmod arduino_gpio_module.ko

You will see Raspberry Pi alter GPIO level for 5 times (once per second).

/dts-v1/;
/plugin/;
/ {
compatible="brcm,brcm2835";
fragment@0 {
target = <&gpio>;
__overlay__ {
arduino_gpio: arduino_gpio_conf {
brcm,pins = <0x11>;
brcm,function = <0x1>;
brcm,pull = <0x1>;
};
};
};
fragment@1 {
target-path = "/";
__overlay__ {
arduino: arduino_gpio {
gpios = <&gpio 0x11 0x0>;
compatible = "arduino";
status = "ok";
pinctrl-0 = <&arduino_gpio>;
pinctrl-names = "default";
};
};
};
};
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
struct job_instance {
int delay;
struct work_struct job;
} jobs[5];
struct arduino {
struct gpio_desc *gpio;
int gpio_state;
} arduino;
void invert_output(struct work_struct *work)
{
struct job_instance *w = container_of(work, struct job_instance, job);
msleep((w->delay) * 1000);
arduino.gpio_state = !gpiod_get_value(arduino.gpio);
pr_info("Inverting GPIO outut to %d.\n", arduino.gpio_state);
gpiod_set_value(arduino.gpio, arduino.gpio_state);
}
static int arduino_probe(struct platform_device *pdev)
{
int i = 0;
pr_info("Arduino gpio consumer being probed.\n");
arduino.gpio = devm_gpiod_get(&(pdev->dev), NULL, GPIOD_OUT_LOW);
if (IS_ERR(arduino.gpio)) {
pr_err("Error when assigning GPIO.\n");
return -22;
}
for (i = 0; i < 5; i++) {
jobs[i].delay = i + 1;
INIT_WORK(&(jobs[i].job), invert_output);
schedule_work(&(jobs[i].job));
}
return 0;
}
static int arduino_remove(struct platform_device *pdev)
{
pr_info("Arduino gpio consumer being removed.\n");
gpiod_set_value(arduino.gpio, 0);
devm_gpiod_put(&(pdev->dev), arduino.gpio);
return 0;
}
static struct of_device_id arduino_id_tables[] = {
{.compatible="arduino", },
{}
};
MODULE_DEVICE_TABLE(of, arduino_id_tables);
static struct platform_driver arduino_drv = {
.probe = arduino_probe,
.remove = arduino_remove,
.driver = {
.name = "Arduino GPIO Consumer",
.owner = THIS_MODULE,
.of_match_table = arduino_id_tables,
},
};
module_platform_driver(arduino_drv);
MODULE_LICENSE("GPL");
#define RPI_DATA_PIN 7
#define DELAY_INTERVAL 250
void setup() {
Serial.begin(9600);
pinMode(RPI_DATA_PIN, INPUT);
}
void loop() {
Serial.println(digitalRead(RPI_DATA_PIN));
delay(DELAY_INTERVAL);
}
PWD := $(shell pwd)
KVERSION := $(shell uname -r)
KERNEL_DIR := /lib/modules/$(shell uname -r)/build
MODULE_NAME = arduino_gpio_module
obj-m := $(MODULE_NAME).o
all:
make -C $(KERNEL_DIR) M=$(PWD) modules
clean:
make -C $(KERNEL_DIR) M=$(PWD) clean
@digitalLumberjack
Copy link

You made my day. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment