Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
688 changes: 688 additions & 0 deletions .clang-format

Large diffs are not rendered by default.

52 changes: 52 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Prerequisites
*.d

# Object files
*.o
*.ko
*.obj
*.elf

# Linker output
*.ilk
*.map
*.exp

# Precompiled Headers
*.gch
*.pch

# Libraries
*.lib
*.a
*.la
*.lo

# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib

# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex

# Debug files
*.dSYM/
*.su
*.idb
*.pdb

# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
21 changes: 21 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

CF = clang-format-12 -i

.PHONY: format check-format check-arg

check-arg:
@if test "$(DIR)" = "" ; then \
echo DIR not set; \
echo Use:;\
echo make cmd DIR=...;\
echo ;\
exit 1; \
fi

format: check-arg
@echo format SRC
$(CF) $(DIR)/*.[ch]

check-format: check-arg
@echo "\n\e[32m*** Check format of source code ***\e[0m\n"
@utils/checkpatch.pl --no-tree -f $(DIR)/*.[ch]
10 changes: 10 additions & 0 deletions Task04_simple_kernel_drive/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
obj-m += encoder_driver.o

KDIR = /lib/modules/$(shell uname -r)/build

all:
make -C $(KDIR) M=$(shell pwd) modules

clean:
make -C $(KDIR) M=$(shell pwd) clean

37 changes: 37 additions & 0 deletions Task04_simple_kernel_drive/ReadMe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Simple encoder Linux Kernel Driver

Build command:

sudo make

For install driver

sudo insmod encoder_driver.ko

For clean build:

sudo make clean

For check and control of results:

dmesg -wH

For read of encoder value:

watch -n 0.1 cat /sys/module/encoder_driver/parameters/encoder_value

For remove driver:

rmmod encoder_driver


## References:

https://tldp.org/LDP/lkmpg/2.6/html/c119.html

https://sysprog21.github.io/lkmpg/#hello-world

https://embetronicx.com/tutorials/linux/device-drivers/gpio-driver-basic-using-raspberry-pi/

https://embetronicx.com/tutorials/linux/device-drivers/gpio-linux-device-driver-using-raspberry-pi/

134 changes: 134 additions & 0 deletions Task04_simple_kernel_drive/encoder_driver.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// SPDX-License-Identifier: GPL-2.0+

/*
* encoder_driver.c - simple encoder driver for linux kernel
*/

#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>

// Pins with encoder
#define GPIO_PIN_A 7
#define GPIO_PIN_B 8

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Yurii.Bezkor");
MODULE_DESCRIPTION("Encoder driver");
MODULE_VERSION("0.2");

int encoder_value; // value encoder

unsigned int gpio_irq; // IRQ number

// Register params
module_param(encoder_value, int, 0664);

/*
* IRQ handler
*/
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
{
static unsigned long flags;

local_irq_save(flags);

if (gpio_get_value(GPIO_PIN_B))
encoder_value++;
else
encoder_value--;

pr_info("encoder_value = %d\n", encoder_value);

local_irq_restore(flags);

return IRQ_HANDLED;
}

/*
* Module init function
*/
static int __init encoder_init(void)
{
// Error code variable
int error_code;

// Check is GPIO valid
if (false == gpio_is_valid(GPIO_PIN_A)) {
pr_err("GPIO_PIN_A = %d not valid\n", GPIO_PIN_A);
return -EINVAL;
}
pr_info("GPIO_PIN_A = %d is valid\n", GPIO_PIN_A);

if (false == gpio_is_valid(GPIO_PIN_B)) {
pr_err("GPIO_PIN_B = %d not valid\n", GPIO_PIN_B);
return -EINVAL;
}
pr_info("GPIO_PIN_B = %d is valid\n", GPIO_PIN_B);

// Try GPIO request
error_code = gpio_request(GPIO_PIN_A, "Pin A");
if (error_code) {
pr_err("GPIO_PIN_A request error\n");
return error_code;
}
pr_info("GPIO_PIN_A = %d request is OK\n", GPIO_PIN_A);

error_code = gpio_request(GPIO_PIN_B, "Pin B");
if (error_code) {
pr_err("GPIO_PIN_B request error\n");
return error_code;
}
pr_info("GPIO_PIN_B = %d request is OK\n", GPIO_PIN_B);

// Config pins as input
gpio_direction_input(GPIO_PIN_A);
pr_info("Config A = %d\n", GPIO_PIN_A);
gpio_direction_input(GPIO_PIN_B);
pr_info("Config B = %d\n", GPIO_PIN_B);

// Config pin A as IRQ pin
gpio_irq = gpio_to_irq(GPIO_PIN_A);
pr_info("Config IRQ = %d\n", gpio_irq);

// Try register IRQ
error_code = request_irq(gpio_irq, (void *)gpio_irq_handler,
IRQF_TRIGGER_FALLING, "encoder_device", NULL);
if (error_code) {
pr_info("Error IRQ request\n");
return error_code;
}
pr_info("IRQ request is OK\n");

// Init default encoder value
encoder_value = 0;
pr_info("encoder_value = %d\n", encoder_value);

pr_info("Encoder Kernel Module Inserted Successfully...\n");
return 0;
}

/*
* Module Exit function
*/
static void __exit encoder_exit(void)
{
free_irq(gpio_irq, NULL);
gpio_free(GPIO_PIN_A);
gpio_free(GPIO_PIN_B);
pr_info("Encoder Kernel Module Removed Successfully...\n");
}

module_init(encoder_init);
module_exit(encoder_exit);

/*
* NOTE:
* Init procedure of GPIO
* gpio_is_valid -> gpio_request -> gpio_direction_input
* can be replaced by a cleaner and more correct call
* gpio_request_one or gpio_request_array
*/
Loading