From ba6acd8c0f4019c92ccd550590fd9dfadf9e69de Mon Sep 17 00:00:00 2001 From: Oleksandr Posukhov Date: Tue, 23 Nov 2021 20:10:08 +0200 Subject: [PATCH 1/7] Task04: Add task for Lection 03 Add home task for lection Basic Data structures Signed-off-by: Oleksandr Posukhov --- 04_basic_struct/README.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 04_basic_struct/README.md diff --git a/04_basic_struct/README.md b/04_basic_struct/README.md new file mode 100644 index 0000000..445d6dc --- /dev/null +++ b/04_basic_struct/README.md @@ -0,0 +1,7 @@ +## Basic structure homework +Implement object with name “MyObject” which is parent of kernel_kobj. +Object should include linked_list structure. +This object should contain sysfs attribute with name “list”. +On read form attribute “list” it should show content of the objects linked list. +On write to attribute “list” it should add new string to the objects linked list. +!! Do not forget properly free all the resources during rmmod. From e260cf649de16aad3755fecf2fe586c60b420e5f Mon Sep 17 00:00:00 2001 From: Yevgen Kovalyov Date: Wed, 1 Dec 2021 00:13:52 +0200 Subject: [PATCH 2/7] Task05: Add tasks for lesson 5 Time Management Add descriptions of tasks for lesson 5. Signed-off-by: Yevgen Kovalyov --- 05_timers/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 05_timers/README.md diff --git a/05_timers/README.md b/05_timers/README.md new file mode 100644 index 0000000..55b41f2 --- /dev/null +++ b/05_timers/README.md @@ -0,0 +1,10 @@ +## Homework: Linux Kernel Time Management + +1. Implement program which return absolute time in user space. +Use clock_gettime() from time.h. Try different clock id. +Find the difference. Show possible clock resolution provided by clock_getres(). + +2. Implement kernel module with API in sysfs, which returns relative +time in maximum possible resolution passed since previous read of it. +Implement kernel module with API in sysfs which returns absolute time +of previous reading with maximum resolution like ‘400.123567’ seconds. From a8bc3010b8552272a6d76d1e62754e0e778b7342 Mon Sep 17 00:00:00 2001 From: Yevgen Kovalyov Date: Thu, 2 Dec 2021 19:36:43 +0200 Subject: [PATCH 3/7] Task06: Add tasks for lesson 6 Memory Management --- 06_memory/README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 06_memory/README.md diff --git a/06_memory/README.md b/06_memory/README.md new file mode 100644 index 0000000..4c22613 --- /dev/null +++ b/06_memory/README.md @@ -0,0 +1,22 @@ +# Memory management + +## Homework +1. Create user-space C or C++ program which tries to allocate buffers + with sizes 2^x for x in range from 0 to maximium possible value + using functions: + **malloc, calloc, alloca, (optional for C++) new **. + Measure time of each allocation/freeing. + 2^x means x power of 2 in this task. +Pull request should contains program source code and program output +in text format. + +2. Create kernel module and test allocation/freeing time for functions: + **kmalloc, kzmalloc, vmalloc, get_free_pages, + (optional and only for drivers integrated to kernel)alloc_bootmem**. + Measure the time of each allocation/freeing except alloc_bootmem. + The results should be presented in text file table with followed columns: + Buffer size, allocation time, freeing time. + Size unit is 1 byte, time unit is 1 ns. + +Pull request should contains source code of developed driver, Makefile +and program output from system log in text format. From 9d1495bac6172bba2727df813a46f9a7de96c8df Mon Sep 17 00:00:00 2001 From: Oleksandr Posukhov Date: Wed, 8 Dec 2021 16:50:33 +0200 Subject: [PATCH 4/7] Task07: Add procfs task for lesson 7 ProcFS and platform bootup Add task for implementation of procFS API Signed-off-by: Oleksandr Posukhov --- 07_procfs/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 07_procfs/README.md diff --git a/07_procfs/README.md b/07_procfs/README.md new file mode 100644 index 0000000..7fa3528 --- /dev/null +++ b/07_procfs/README.md @@ -0,0 +1,10 @@ +## Lesson 07 - ProcFS interface and orange pi bootup + +* Update your existing sysfs kernel module with procfs API: +* Create folder in procfs file system; +* Create entry that returns module author name; +* Create entry that returns amount of "store" callback calls; +* Create entry that returns amount of "show" callback calls. +* Build image for orange pi zero +* Attach console output from your development board + From b0c236f9a37f4bd8465d78fadb03c1810e8e28bb Mon Sep 17 00:00:00 2001 From: Oleksandr Posukhov Date: Tue, 21 Dec 2021 14:14:22 +0200 Subject: [PATCH 5/7] Lesson08: Add home task 08 Add example of polling mechanism, add hometask Signed-off-by: Oleksandr Posukhov --- 08_irq_handling/README.md | 5 ++ 08_irq_handling/led_mod.c | 154 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 08_irq_handling/README.md create mode 100644 08_irq_handling/led_mod.c diff --git a/08_irq_handling/README.md b/08_irq_handling/README.md new file mode 100644 index 0000000..20360a8 --- /dev/null +++ b/08_irq_handling/README.md @@ -0,0 +1,5 @@ +## Lesson 08 - IRQ handling + +Run polling example on your board. +Modify you driver to enable irq handling instead of polling mechanism + diff --git a/08_irq_handling/led_mod.c b/08_irq_handling/led_mod.c new file mode 100644 index 0000000..5444623 --- /dev/null +++ b/08_irq_handling/led_mod.c @@ -0,0 +1,154 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include +#include +#include +#include + +#define GPIO_NUMBER(port, bit) (32 * (port) + (bit)) + +/* https://linux-sunxi.org/Xunlong_Orange_Pi_PC#LEDs + * Board config for OPI-PC: + * LED GREEN (PL10): GPIO_11_10 + * LED RED (PA15): GPIO_0_15 + * BUTTON (PG7) : GPIO_6_7 + * + * https://linux-sunxi.org/Xunlong_Orange_Pi_Zero#LEDs + * Board config for OPI-Zero: + * LED GREEN (PL10): GPIO_11_10 + * LED RED (PA17): GPIO_0_17 + * BUTTON (PG7) : GPIO_6_7 + * + */ + +#define LED_GREEN GPIO_NUMBER(11, 10) +#define LED_RED GPIO_NUMBER(0, 15) +#define BUTTON GPIO_NUMBER(6, 7) + +//#define TIMER_ENABLE 1 + +static int ledg_gpio = -1; +static int ledr_gpio = -1; +static int button_gpio = -1; +static int button_state = -1; +static int button_cnt = -1; + +#ifdef TIMER_ENABLE +static ktime_t timer_period; +struct hrtimer button_timer; + +static enum hrtimer_restart timer_callback(struct hrtimer *timer) +{ + int cur_button_state; + + cur_button_state = gpio_get_value(button_gpio); + button_cnt = (cur_button_state == button_state) ? (button_cnt + 1) : 0; + button_state = cur_button_state; + gpio_set_value(ledr_gpio, ((button_cnt == 20) ? 1 : 0)); + if (button_cnt >= 20) + gpio_set_value(ledg_gpio, !button_state); + hrtimer_forward(timer, timer->base->get_time(), timer_period); + return HRTIMER_RESTART; //restart timer +} +#endif + +static int led_gpio_init(int gpio, int *led_gpio) +{ + int res; + + res = gpio_direction_output(gpio, 0); + if (res != 0) + return res; + + *led_gpio = gpio; + return 0; +} + +static int button_gpio_init(int gpio) +{ + int res; + + res = gpio_request(gpio, "Onboard user button"); + if (res != 0) + return res; + + res = gpio_direction_input(gpio); + if (res != 0) + goto err_input; + + button_gpio = gpio; + pr_info("Init GPIO%d OK\n", button_gpio); + button_state = gpio_get_value(button_gpio); + button_cnt = 0; + + return 0; + +err_input: + gpio_free(gpio); + return res; +} + +static void button_gpio_deinit(void) +{ + if (button_gpio >= 0) { + gpio_free(button_gpio); + pr_info("Deinit GPIO%d\n", button_gpio); + } +} + +/* Module entry/exit points */ +static int __init gpio_poll_init(void) +{ + int res; + pr_info("GPIO Init\n"); + + res = button_gpio_init(BUTTON); + if (res != 0) { + pr_err("Can't set GPIO%d for button\n", BUTTON); + return res; + } +#ifdef TIMER_ENABLE + timer_period = ktime_set(0, 1000000); /*1 msec*/ + hrtimer_init(&button_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer_start(&button_timer, timer_period, HRTIMER_MODE_REL); + button_timer.function = timer_callback; +#endif + res = led_gpio_init(LED_GREEN, &ledg_gpio); + if (res != 0) { + pr_err("Can't set GPIO%d for output\n", LED_GREEN); + goto err_led; + } + + gpio_set_value(ledg_gpio, 0); + + res = led_gpio_init(LED_RED, &ledr_gpio); + if (res != 0) { + pr_err("Can't set GPIO%d for output\n", LED_RED); + goto err_led; + } + gpio_set_value(ledr_gpio, 1); + + return 0; + +err_led: + button_gpio_deinit(); + return res; +} + +static void __exit gpio_poll_exit(void) +{ + gpio_set_value(ledg_gpio, 0); + gpio_set_value(ledr_gpio, 0); + button_gpio_deinit(); +#ifdef TIMER_ENABLE + hrtimer_cancel(&button_timer); +#endif +} + +module_init(gpio_poll_init); +module_exit(gpio_poll_exit); + +MODULE_AUTHOR("Oleksandr Posukhov oleksandr.posukhov@gmail.com>"); +MODULE_DESCRIPTION("LED Test"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); \ No newline at end of file From d552a3919932244fed0c4010b003bd39d5db0299 Mon Sep 17 00:00:00 2001 From: Oleksandr Posukhov Date: Tue, 21 Dec 2021 14:22:50 +0200 Subject: [PATCH 6/7] Lesson10: Home task for character device driver Add hometask for lection 10: Chardev Signed-off-by: Oleksandr Posukhov --- 10_chardev/REAMDE.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 10_chardev/REAMDE.md diff --git a/10_chardev/REAMDE.md b/10_chardev/REAMDE.md new file mode 100644 index 0000000..a4fbf99 --- /dev/null +++ b/10_chardev/REAMDE.md @@ -0,0 +1,4 @@ +## Task10: Character device + +Create character device driver and userspace application to control status led and user led using gpio:w... + From cd2d13e9b54ce1aa272ad68dc32343b835888f0b Mon Sep 17 00:00:00 2001 From: d-serj Date: Sun, 16 Jan 2022 12:35:17 -0800 Subject: [PATCH 7/7] Task10: Implement module and application to control gpio Implement character device kernel module to allow control of builtin Orange PI Zero leds. Implement userspace application to control Orange PI Zero builtin leds. Signed-off-by: d-serj --- 08_irq_handling/led_mod.c | 154 ---------------------- 10_chardev/Makefile | 18 +++ 10_chardev/chrdev_mod.c | 244 +++++++++++++++++++++++++++++++++++ 10_chardev/dev.h | 35 +++++ 10_chardev/ioctrl_usrspace.c | 78 +++++++++++ 5 files changed, 375 insertions(+), 154 deletions(-) delete mode 100644 08_irq_handling/led_mod.c create mode 100644 10_chardev/Makefile create mode 100644 10_chardev/chrdev_mod.c create mode 100644 10_chardev/dev.h create mode 100644 10_chardev/ioctrl_usrspace.c diff --git a/08_irq_handling/led_mod.c b/08_irq_handling/led_mod.c deleted file mode 100644 index 5444623..0000000 --- a/08_irq_handling/led_mod.c +++ /dev/null @@ -1,154 +0,0 @@ -#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ - -#include -#include -#include -#include - -#define GPIO_NUMBER(port, bit) (32 * (port) + (bit)) - -/* https://linux-sunxi.org/Xunlong_Orange_Pi_PC#LEDs - * Board config for OPI-PC: - * LED GREEN (PL10): GPIO_11_10 - * LED RED (PA15): GPIO_0_15 - * BUTTON (PG7) : GPIO_6_7 - * - * https://linux-sunxi.org/Xunlong_Orange_Pi_Zero#LEDs - * Board config for OPI-Zero: - * LED GREEN (PL10): GPIO_11_10 - * LED RED (PA17): GPIO_0_17 - * BUTTON (PG7) : GPIO_6_7 - * - */ - -#define LED_GREEN GPIO_NUMBER(11, 10) -#define LED_RED GPIO_NUMBER(0, 15) -#define BUTTON GPIO_NUMBER(6, 7) - -//#define TIMER_ENABLE 1 - -static int ledg_gpio = -1; -static int ledr_gpio = -1; -static int button_gpio = -1; -static int button_state = -1; -static int button_cnt = -1; - -#ifdef TIMER_ENABLE -static ktime_t timer_period; -struct hrtimer button_timer; - -static enum hrtimer_restart timer_callback(struct hrtimer *timer) -{ - int cur_button_state; - - cur_button_state = gpio_get_value(button_gpio); - button_cnt = (cur_button_state == button_state) ? (button_cnt + 1) : 0; - button_state = cur_button_state; - gpio_set_value(ledr_gpio, ((button_cnt == 20) ? 1 : 0)); - if (button_cnt >= 20) - gpio_set_value(ledg_gpio, !button_state); - hrtimer_forward(timer, timer->base->get_time(), timer_period); - return HRTIMER_RESTART; //restart timer -} -#endif - -static int led_gpio_init(int gpio, int *led_gpio) -{ - int res; - - res = gpio_direction_output(gpio, 0); - if (res != 0) - return res; - - *led_gpio = gpio; - return 0; -} - -static int button_gpio_init(int gpio) -{ - int res; - - res = gpio_request(gpio, "Onboard user button"); - if (res != 0) - return res; - - res = gpio_direction_input(gpio); - if (res != 0) - goto err_input; - - button_gpio = gpio; - pr_info("Init GPIO%d OK\n", button_gpio); - button_state = gpio_get_value(button_gpio); - button_cnt = 0; - - return 0; - -err_input: - gpio_free(gpio); - return res; -} - -static void button_gpio_deinit(void) -{ - if (button_gpio >= 0) { - gpio_free(button_gpio); - pr_info("Deinit GPIO%d\n", button_gpio); - } -} - -/* Module entry/exit points */ -static int __init gpio_poll_init(void) -{ - int res; - pr_info("GPIO Init\n"); - - res = button_gpio_init(BUTTON); - if (res != 0) { - pr_err("Can't set GPIO%d for button\n", BUTTON); - return res; - } -#ifdef TIMER_ENABLE - timer_period = ktime_set(0, 1000000); /*1 msec*/ - hrtimer_init(&button_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - hrtimer_start(&button_timer, timer_period, HRTIMER_MODE_REL); - button_timer.function = timer_callback; -#endif - res = led_gpio_init(LED_GREEN, &ledg_gpio); - if (res != 0) { - pr_err("Can't set GPIO%d for output\n", LED_GREEN); - goto err_led; - } - - gpio_set_value(ledg_gpio, 0); - - res = led_gpio_init(LED_RED, &ledr_gpio); - if (res != 0) { - pr_err("Can't set GPIO%d for output\n", LED_RED); - goto err_led; - } - gpio_set_value(ledr_gpio, 1); - - return 0; - -err_led: - button_gpio_deinit(); - return res; -} - -static void __exit gpio_poll_exit(void) -{ - gpio_set_value(ledg_gpio, 0); - gpio_set_value(ledr_gpio, 0); - button_gpio_deinit(); -#ifdef TIMER_ENABLE - hrtimer_cancel(&button_timer); -#endif -} - -module_init(gpio_poll_init); -module_exit(gpio_poll_exit); - -MODULE_AUTHOR("Oleksandr Posukhov oleksandr.posukhov@gmail.com>"); -MODULE_DESCRIPTION("LED Test"); -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.1"); \ No newline at end of file diff --git a/10_chardev/Makefile b/10_chardev/Makefile new file mode 100644 index 0000000..bdc3501 --- /dev/null +++ b/10_chardev/Makefile @@ -0,0 +1,18 @@ +KERNELDIR ?= ../../output/build/linux-5.10.10/ #WARNING relative path + +TARGET = chrdev_mod + +obj-m := $(TARGET).o +CFLAGS_$(TARGET).o := -std=gnu11 -DDEBUG -Wno-declaration-after-statement + +all: clean build ioctrl + +build: + $(MAKE) -C $(KERNELDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KERNELDIR) M=$(PWD) clean + rm -rf ioctrl_usrspace + +ioctrl: + $(CROSS_COMPILE)gcc ioctrl_usrspace.c -o ioctrl_usrspace diff --git a/10_chardev/chrdev_mod.c b/10_chardev/chrdev_mod.c new file mode 100644 index 0000000..6edd2f5 --- /dev/null +++ b/10_chardev/chrdev_mod.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dev.h" + +#define CHRDEV_MODULE_AUTHOR "Sergey Dubyna " + +#define GPIO_NUMBER(port, bit) (32 * (port) + (bit)) + +/* + * https://linux-sunxi.org/Xunlong_Orange_Pi_Zero#LEDs + * Board config for OPI-Zero: + * LED GREEN (PL10): GPIO_11_10 + * LED RED (PA17): GPIO_0_17 + */ + +#define LED_GREEN GPIO_NUMBER(11, 10) +#define LED_RED GPIO_NUMBER(0, 17) + +#define CHRDEV_DEV_NAME "chrdev_led" +#define CHRDEV_CLASS_NAME "chrdev" + +#define BUFFER_SIZE 1024u + +static int data_size; +static u8 data_buffer[BUFFER_SIZE]; + +static int ledg_gpio = -1; +static int ledr_gpio = -1; + +static struct class *pclass; +static struct device *pdev; +static int major; +static int is_open; + +static int led_gpio_init(int gpio, int *led_gpio) +{ + int res; + + res = gpio_direction_output(gpio, 0); + if (res != 0) + return res; + + *led_gpio = gpio; + return 0; +} + +static int chrdev_open(struct inode *pinode, struct file *pfile) +{ + (void)pinode; + (void)pfile; + + if (is_open) { + pr_err("device is already opened\n"); + return -EBUSY; + } + + is_open = 1; + pr_info("device opened\n"); + return 0; +} + +static int chrdev_release(struct inode *pinode, struct file *pfile) +{ + (void)pinode; + (void)pfile; + + is_open = 0; + pr_info("device closed\n"); + return 0; +} + +static ssize_t chrdev_read(struct file *pfile, char *buf, size_t len, loff_t *offset) +{ + int ret; + + pr_info("read from file %s\n", pfile->f_path.dentry->d_iname); + pr_info("read from device %d:%d\n", imajor(pfile->f_inode), iminor(pfile->f_inode)); + + if (len > data_size) + len = data_size; + + ret = copy_to_user(buf, data_buffer, len); + if (ret) { + pr_err("copy_to_user failed: %d\n", ret); + return -EFAULT; + } + + data_size = 0; + pr_info("%zu bytes read\n", len); + return len; +} + +static ssize_t chrdev_write(struct file *pfile, const char *buf, size_t len, loff_t *offset) +{ + int ret; + + pr_info("write to file %s\n", pfile->f_path.dentry->d_iname); + pr_info("write to device %d:%d\n", imajor(pfile->f_inode), iminor(pfile->f_inode)); + + data_size = len; + if (data_size > BUFFER_SIZE) + data_size = BUFFER_SIZE; + + ret = copy_from_user(data_buffer, buf, data_size); + if (ret) { + pr_err("copy_from_user failed: %d\n", ret); + return -EFAULT; + } + + pr_info("%d bytes written\n", data_size); + return data_size; +} + +static long chrdev_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + struct led_control led_cmd = {}; + + (void)f; + + if (_IOC_TYPE(cmd) != CHRDEV_IOCTRL_MAGIC) + return -ENOTTY; + + if (copy_from_user(&led_cmd, (const void *)arg, sizeof(struct led_control))) + return -EFAULT; + + if (!chrdev_led_cmd_is_ok(&led_cmd)) { + BUG(); + return -EINVAL; + } + + const int gpio_num = + (led_cmd.e_type == e_led_type_green) ? ledg_gpio : ledr_gpio; + + switch (cmd) { + case CHRDEV_IOCTRL_SET_VAL: + gpio_set_value(gpio_num, led_cmd.e_state); + break; + + case CHRDEV_IOCTRL_GET_VAL: + led_cmd.e_state = + gpio_get_value(gpio_num) ? e_led_state_on : e_led_state_off; + if (copy_to_user((void *)arg, &led_cmd, sizeof(struct led_control))) + return -EFAULT; + break; + + default: + BUG(); + return -EINVAL; + } + + return 0; +} + +static const struct file_operations chrdev_fops = { + .open = chrdev_open, + .write = chrdev_write, + .release = chrdev_release, + .read = chrdev_read, + .unlocked_ioctl = chrdev_ioctl, +}; + +static int __init chrdev_mod_init(void) +{ + int ret; + + is_open = 0; + data_size = 0; + + major = register_chrdev(0, CHRDEV_DEV_NAME, &chrdev_fops); + if (major < 0) { + pr_err("Error %d. Cannot register char device\n", major); + return major; + } + pr_info("Chrdev registered, major = %d\n", major); + + pclass = class_create(THIS_MODULE, CHRDEV_CLASS_NAME); + if (IS_ERR(pclass)) { + unregister_chrdev(major, CHRDEV_DEV_NAME); + pr_err("Cannot create class\n"); + return PTR_ERR(pclass); + } + pr_info("Class successfully created\n"); + + pdev = device_create(pclass, NULL, MKDEV(major, 0), NULL, CHRDEV_CLASS_NAME "0"); + if (IS_ERR(pdev)) { + class_destroy(pclass); + unregister_chrdev(major, CHRDEV_DEV_NAME); + pr_err("Cannot create device\n"); + return PTR_ERR(pdev); + } + pr_info("Device node created successfully\n"); + + ret = led_gpio_init(LED_GREEN, &ledg_gpio); + if (ret != 0) { + pr_err("Cannot set GPIO%d for output\n", LED_GREEN); + return ret; + } + + ret = led_gpio_init(LED_RED, &ledr_gpio); + if (ret != 0) { + pr_err("Can't set GPIO%d for output\n", LED_RED); + return ret; + } + + gpio_set_value(ledg_gpio, 0); + gpio_set_value(ledr_gpio, 0); + + pr_info("module loaded\n"); + + return 0; +} + +static void __exit chrdev_mod_exit(void) +{ + device_destroy(pclass, MKDEV(major, 0)); + class_destroy(pclass); + unregister_chrdev(major, CHRDEV_DEV_NAME); + + gpio_set_value(ledg_gpio, 0); + gpio_set_value(ledr_gpio, 0); + gpio_free(ledg_gpio); + gpio_free(ledr_gpio); + + pr_info("module exited\n"); +} + +module_init(chrdev_mod_init); +module_exit(chrdev_mod_exit); + +MODULE_AUTHOR(CHRDEV_MODULE_AUTHOR); +MODULE_DESCRIPTION("Char device module"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); diff --git a/10_chardev/dev.h b/10_chardev/dev.h new file mode 100644 index 0000000..176a0d8 --- /dev/null +++ b/10_chardev/dev.h @@ -0,0 +1,35 @@ +#ifndef DEV_H_ +#define DEV_H_ + +enum e_led_state +{ + e_led_state_off = 0, + e_led_state_on, +}; + +enum e_led_type +{ + e_led_type_green = 0, + e_led_type_red, +}; + +struct led_control +{ + enum e_led_type e_type; + enum e_led_state e_state; +}; + +#define CHRDEV_IOCTRL_MAGIC 's' +/** Set LED state */ +#define CHRDEV_IOCTRL_SET_VAL _IOW(CHRDEV_IOCTRL_MAGIC, 1, const struct led_control *) +/** Get LED state */ +#define CHRDEV_IOCTRL_GET_VAL _IOR(CHRDEV_IOCTRL_MAGIC, 2, struct led_control *) +#define CHRDEV_PATH "/dev/chrdev0" + +static inline int chrdev_led_cmd_is_ok(const struct led_control *pled_cmd) +{ + return (pled_cmd->e_type == e_led_type_green || pled_cmd->e_type == e_led_type_red) + && (pled_cmd->e_state == e_led_state_off || pled_cmd->e_state == e_led_state_on); +} + +#endif // DEV_H_ diff --git a/10_chardev/ioctrl_usrspace.c b/10_chardev/ioctrl_usrspace.c new file mode 100644 index 0000000..f1deb90 --- /dev/null +++ b/10_chardev/ioctrl_usrspace.c @@ -0,0 +1,78 @@ +/** + * @file ioctrl_usrspace.c + * @brief User space appliation to control builtin Orange PI leds + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dev.h" + +static int cmd_run = 1; + +static int blink(int dfile, enum e_led_type type); +static void signal_handler(int signal); + +int main(void) +{ + int dfd; + + signal(SIGINT, signal_handler); + + if ((dfd = open(CHRDEV_PATH, O_RDWR)) < 0) { + fprintf(stderr, "Error %d while opening the device\n", dfd); + exit(EXIT_FAILURE); + } + + while (cmd_run) { + if (blink(dfd, e_led_type_green)) + break; + + if (blink(dfd, e_led_type_red)) + break; + + usleep(100000); + } + + close(dfd); + signal(SIGINT, SIG_DFL); + + return EXIT_SUCCESS; +} + +static int blink(int dfile, enum e_led_type type) +{ + struct led_control led_ctrl = { .e_type = type }; + + if (ioctl(dfile, CHRDEV_IOCTRL_GET_VAL, &led_ctrl)) { + fprintf(stderr, "Error %d while ioctl get operation\n", dfile); + return dfile; + } + + if (!chrdev_led_cmd_is_ok(&led_ctrl)) { + assert(0); + return -1; + } + + /* Invert LED state */ + led_ctrl.e_state = + (led_ctrl.e_state == e_led_state_off) ? e_led_state_on : e_led_state_off; + + if (ioctl(dfile, CHRDEV_IOCTRL_SET_VAL, &led_ctrl)) { + fprintf(stderr, "Error %d while ioctl set operation\n", dfile); + return dfile; + } + + return 0; +} + +static void signal_handler(int signal) +{ + cmd_run = 0; +}