diff --git a/.gitignore b/.gitignore index 4af57f4..32e0afc 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,6 @@ driver/*.mod.c driver/*.mod.o driver/*.o driver/modules.order -dtoverlay/*.dtbo +driver/*.dtbo diff --git a/driver/Makefile b/driver/Makefile index 1233a6e..09fe964 100644 --- a/driver/Makefile +++ b/driver/Makefile @@ -1,11 +1,30 @@ -obj-m += counter.o +NAME = counter +obj-m += $(NAME).o -KDIR := /lib/modules/$(shell uname -r)/build -PWD := $(shell pwd) +all: $(NAME).ko $(NAME).dtbo + echo Builded Device Tree Overlay and kernel module -all: - $(MAKE) -C $(KDIR) M=$(PWD) modules +$(NAME).ko: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +$(NAME).dtbo: $(NAME).dts + dtc -@ -I dts -O dtb -o $@ $< clean: - $(MAKE) -C $(KDIR) M=$(PWD) clean + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean + rm -rf $(NAME).dtbo + +load: + sudo dtoverlay -d . $(NAME).dtbo + @echo "Overlay loaded" + sudo insmod ./$(NAME).ko + @echo "LKM loaded" +unload: + sudo rmmod ./$(NAME).ko + @echo "LKM unloaded" + sudo dtoverlay -R $(NAME) + @echo "Overlay unloaded" + +list: + sudo dtoverlay -l diff --git a/driver/counter.c b/driver/counter.c index 4c78fef..1cc91fd 100644 --- a/driver/counter.c +++ b/driver/counter.c @@ -1,59 +1,147 @@ -#include #include -#include -#include +#include +#include +#include #include +#include +#include +#include -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Du"); -MODULE_DESCRIPTION("GPIO consumer API example for two LEDs"); +/* Meta Information */ +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Johannes 4 GNU/Linux"); +MODULE_DESCRIPTION("A simple LKM to parse the device tree for a specific device and its properties"); -static struct gpio_desc *led_blue; -static struct gpio_desc *led_red; +/* Declate the probe and remove functions */ +static int dt_probe(struct platform_device *pdev); +static void dt_remove(struct platform_device *pdev); -static int __init led_consumer_init(void) -{ - struct device *dev = NULL; +static struct of_device_id my_driver_ids[] = { + { + .compatible = "brightlight,mydev", + }, { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, my_driver_ids); - pr_info("GPIO consumer LED module loading\n"); +static struct platform_driver my_driver = { + .probe = dt_probe, + .remove = dt_remove, + .driver = { + .name = "my_device_driver", + .of_match_table = my_driver_ids, + }, +}; - // GPIOs per Name aus dem Device Tree holen (via label oder node property) - led_blue = gpiod_get(dev, "led-blue", GPIOD_OUT_LOW); - if (IS_ERR(led_blue)) { - pr_err("Failed to get GPIO for blue LED\n"); - return PTR_ERR(led_blue); - } +/* GPIO variable */ +static struct gpio_desc *my_led = NULL; - led_red = gpiod_get(dev, "led-red", GPIOD_OUT_LOW); - if (IS_ERR(led_red)) { - pr_err("Failed to get GPIO for red LED\n"); - gpiod_put(led_blue); - return PTR_ERR(led_red); - } +static struct proc_dir_entry *proc_file; - // Test: LEDs einschalten - gpiod_set_value(led_blue, 1); - gpiod_set_value(led_red, 1); - msleep(500); - gpiod_set_value(led_blue, 0); - gpiod_set_value(led_red, 0); - - pr_info("LEDs blinked using gpio/consumer\n"); - - return 0; +/** + * @brief Write data to buffer + */ +static ssize_t my_write(struct file *File, const char *user_buffer, size_t count, loff_t *offs) { + switch (user_buffer[0]) { + case '0': + case '1': + gpiod_set_value(my_led, user_buffer[0] - '0'); + default: + break; + } + return count; } -static void __exit led_consumer_exit(void) -{ - gpiod_set_value(led_blue, 0); - gpiod_set_value(led_red, 0); +static struct proc_ops fops = { + .proc_write = my_write, +}; - gpiod_put(led_blue); - gpiod_put(led_red); +/** + * @brief This function is called on loading the driver + */ +static int dt_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + const char *label; + int my_value, ret; - pr_info("GPIO consumer LED module unloaded\n"); + printk("dt_gpio - Now I am in the probe function!\n"); + + /* Check for device properties */ + if(!device_property_present(dev, "label")) { + printk("dt_gpio - Error! Device property 'label' not found!\n"); + return -1; + } + if(!device_property_present(dev, "my_value")) { + printk("dt_gpio - Error! Device property 'my_value' not found!\n"); + return -1; + } + if(!device_property_present(dev, "green-led-gpio")) { + printk("dt_gpio - Error! Device property 'green-led-gpio' not found!\n"); + return -1; + } + + /* Read device properties */ + ret = device_property_read_string(dev, "label", &label); + if(ret) { + printk("dt_gpio - Error! Could not read 'label'\n"); + return -1; + } + printk("dt_gpio - label: %s\n", label); + ret = device_property_read_u32(dev, "my_value", &my_value); + if(ret) { + printk("dt_gpio - Error! Could not read 'my_value'\n"); + return -1; + } + printk("dt_gpio - my_value: %d\n", my_value); + + /* Init GPIO */ + my_led = gpiod_get(dev, "green-led", GPIOD_OUT_LOW); + if(IS_ERR(my_led)) { + printk("dt_gpio - Error! Could not setup the GPIO\n"); + return -1 * IS_ERR(my_led); + } + + /* Creating procfs file */ + proc_file = proc_create("my-led", 0666, NULL, &fops); + if(proc_file == NULL) { + printk("procfs_test - Error creating /proc/my-led\n"); + gpiod_put(my_led); + return -ENOMEM; + } + + + return 0; } -module_init(led_consumer_init); -module_exit(led_consumer_exit); +/** + * @brief This function is called on unloading the driver + */ +static void dt_remove(struct platform_device *pdev) { + printk("dt_gpio - Now I am in the remove function\n"); + gpiod_put(my_led); + proc_remove(proc_file); +} + +/** + * @brief This function is called, when the module is loaded into the kernel + */ +static int __init my_init(void) { + printk("dt_gpio - Loading the driver...\n"); + if(platform_driver_register(&my_driver)) { + printk("dt_gpio - Error! Could not load driver\n"); + return -1; + } + return 0; +} + +/** + * @brief This function is called, when the module is removed from the kernel + */ +static void __exit my_exit(void) { + printk("dt_gpio - Unload driver"); + platform_driver_unregister(&my_driver); +} + +module_init(my_init); +module_exit(my_exit); + diff --git a/driver/counter.dts b/driver/counter.dts new file mode 100644 index 0000000..8596114 --- /dev/null +++ b/driver/counter.dts @@ -0,0 +1,17 @@ +/dts-v1/; +/plugin/; +/ { + compatible = "brcm,bcm2835"; + fragment@0 { + target-path = "/"; + __overlay__ { + my_device { + compatible = "brightlight,mydev"; + status = "okay"; + label = "Test"; + my_value = <12>; + green-led-gpio = <&gpio 21 0>; + }; + }; + }; +}; diff --git a/dtoverlay/Makefile b/dtoverlay/Makefile deleted file mode 100644 index bdcc50a..0000000 --- a/dtoverlay/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -# Source and target file names -DTS = counter.dts -DTBO = counter.dtbo - -# Installation paths -OVERLAY_DIR = /boot/overlays -CONFIG_TXT = /boot/firmware/config.txt -OVERLAY_NAME = counter - -all: $(DTBO) - -$(DTBO): $(DTS) - dtc -@ -I dts -O dtb -o $@ $< - -load: $(DTBO) - sudo dtoverlay -d . $(OVERLAY_NAME) - @echo "Overlay loaded" - -unload: $(DTBO) - sudo dtoverlay -R $(OVERLAY_NAME) - @echo "Overlay unloaded" - -list: - sudo dtoverlay -l - - -clean: - rm -f *.dtbo - diff --git a/dtoverlay/counter.dts b/dtoverlay/counter.dts deleted file mode 100644 index 1d4c0c7..0000000 --- a/dtoverlay/counter.dts +++ /dev/null @@ -1,46 +0,0 @@ -/dts-v1/; -/plugin/; - -/ { - compatible = "brcm,bcm2835"; - - fragment@0 { - target = <&gpio>; - __overlay__ { - gpio_led_blue: gpio_led_blues@26 { - brcm,pins = <26>; - brcm,function = <1>; // output - brcm,pull = <0>; // no pull - }; - gpio_led_red: gpio_led_reds@21 { - brcm,pins = <21>; - brcm,function = <1>; // output - brcm,pull = <0>; // no pull - }; - }; - }; - - fragment@1 { - target-path = "/"; - __overlay__ { - mygpioleds { - compatible = "my,gpio-led-controller"; - pinctrl-names = "default"; - pinctrl-0 = <&gpio_led_blue &gpio_led_red>; - - led-blue { - gpios = <&gpio 26 0>; - label = "led-blue"; - }; - - led-red { - gpios = <&gpio 21 0>; - label = "led-red"; - }; - }; - }; - }; - -}; - -