diff --git a/src/06-mini-project/daemon/.clangd b/src/06-mini-project/daemon/.clangd new file mode 100644 index 0000000..2322919 --- /dev/null +++ b/src/06-mini-project/daemon/.clangd @@ -0,0 +1,7 @@ +CompileFlags: + Add: + # Architecture and cross-compilation + - "--target=aarch64-linux-gnu" + + # Setup sysroot for buildroot + - "--sysroot=/buildroot/output/host/aarch64-buildroot-linux-gnu/sysroot" diff --git a/src/06-mini-project/daemon/gpio/button.c b/src/06-mini-project/daemon/gpio/button.c new file mode 100644 index 0000000..87d329b --- /dev/null +++ b/src/06-mini-project/daemon/gpio/button.c @@ -0,0 +1,44 @@ +#include "button.h" + +#include +#include +#include +#include +#include + + +#define GPIO_EXPORT "/sys/class/gpio/export" +#define GPIO_UNEXPORT "/sys/class/gpio/unexport" + +int btn_open(const char* gpio_path, const char* pin) { + int f = open(GPIO_UNEXPORT, O_WRONLY); + write(f, pin, strlen(pin)); + close(f); + + f = open(GPIO_EXPORT, O_WRONLY); + write(f, pin, strlen(pin)); + close(f); + + char direction_path[100]; + strcpy(direction_path, gpio_path); + strcat(direction_path, "/direction"); + + f = open(direction_path, O_WRONLY); + write(f, "in", 2); + close(f); + + char edge_path[100]; + strcpy(edge_path, gpio_path); + strcat(edge_path, "/edge"); + + f = open(edge_path, O_WRONLY); + write(f, "both", 4); // "both" means it triggers on press AND release + close(f); + + char value_path[100]; + strcpy(value_path, gpio_path); + strcat(value_path, "/value"); + + f = open(value_path, O_RDONLY); + return f; +} diff --git a/src/06-mini-project/daemon/gpio/button.h b/src/06-mini-project/daemon/gpio/button.h new file mode 100644 index 0000000..4804300 --- /dev/null +++ b/src/06-mini-project/daemon/gpio/button.h @@ -0,0 +1,10 @@ +#ifndef BUTTON_H +#define BUTTON_H + +#define GPIO_EXPORT "/sys/class/gpio/export" +#define GPIO_UNEXPORT "/sys/class/gpio/unexport" + +int btn_open(const char* gpio_path, const char* pin); + + +#endif diff --git a/src/06-mini-project/daemon/gpio/led.c b/src/06-mini-project/daemon/gpio/led.c new file mode 100644 index 0000000..701ee0b --- /dev/null +++ b/src/06-mini-project/daemon/gpio/led.c @@ -0,0 +1,48 @@ +#include "led.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +int led_open(const char* gpio_path, const char* pin) { + + // unexport pin out of sysfs (reinitialization) + int f = open(GPIO_UNEXPORT, O_WRONLY); + write(f, pin, strlen(pin)); + close(f); + + // export pin to sysfs + f = open(GPIO_EXPORT, O_WRONLY); + write(f, pin, strlen(pin)); + close(f); + + // config pin + char direction_path[100]; + strcpy(direction_path, gpio_path); + strcat(direction_path, "/direction"); + + f = open(direction_path, O_WRONLY); + write(f, "out", 3); + close(f); + + // open gpio value attribute + char value_path[100]; + strcpy(value_path, gpio_path); + strcat(value_path, "/value"); + + f = open(value_path, O_RDWR); + return f; +} + +void led_on(int led) { + pwrite(led, "1", sizeof("1"), 0); +} + +void led_off(int led) { + pwrite(led, "0", sizeof("0"), 0); +} diff --git a/src/06-mini-project/daemon/gpio/led.h b/src/06-mini-project/daemon/gpio/led.h new file mode 100644 index 0000000..e299c95 --- /dev/null +++ b/src/06-mini-project/daemon/gpio/led.h @@ -0,0 +1,14 @@ +#ifndef CSEL_WORKSPACE_LED_H +#define CSEL_WORKSPACE_LED_H + +#define GPIO_EXPORT "/sys/class/gpio/export" +#define GPIO_UNEXPORT "/sys/class/gpio/unexport" + +#define GPIO_LED "/sys/class/gpio/gpio10" +#define LED "10" + +int led_open(const char* gpio_path, const char* pin); +void led_on(int led); +void led_off(int led); + +#endif //CSEL_WORKSPACE_LED_H diff --git a/src/06-mini-project/daemon/main.c b/src/06-mini-project/daemon/main.c new file mode 100644 index 0000000..3c2acc9 --- /dev/null +++ b/src/06-mini-project/daemon/main.c @@ -0,0 +1,232 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "timer/timer.h" +#include "gpio/led.h" +#include "gpio/button.h" + +/* + * status led - gpioa.10 --> gpio10 + * power led - gpiol.10 --> gpio362 + */ + +#define GPIO_LED "/sys/class/gpio/gpio10" +#define LED "10" + +#define GPIO_BTN1 "/sys/class/gpio/gpio0" +#define BTN1 "0" +#define GPIO_BTN2 "/sys/class/gpio/gpio2" +#define BTN2 "2" +#define GPIO_BTN3 "/sys/class/gpio/gpio3" +#define BTN3 "3" + +#define NBR_BTN 3 + +#define DEFAULT_TIME_MS 1000 +#define DUTY_CYCLE_PERCENT 2 + + +typedef struct { + long flash_period_ms; + int timer_fd; + int epoll_fd; +} ThreadData; + +// constant +const char* GPIO_BTN[NBR_BTN] = {GPIO_BTN1, GPIO_BTN2, GPIO_BTN3}; +const char* BTN[NBR_BTN] = {BTN1, BTN2, BTN3}; + + +void* btn_thread(void* arg) { + ThreadData* data = (ThreadData*)arg; + + // Open all button with the right flags + int btn[NBR_BTN] = {0}; + for(int i=0; iflash_period_ms += 200; + char* log_msg = malloc(100); + snprintf(log_msg, 100, "Increase flash period to %ld ms", data->flash_period_ms); + syslog(LOG_INFO, "%s", log_msg); + printf("%s\n", log_msg); + free(log_msg); + } + + } else if (events[i].data.fd == btn[1]) { + if (buf[0] == '1') { + data->flash_period_ms = DEFAULT_TIME_MS; + char* log_msg = malloc(100); + snprintf(log_msg, 100, "Reset flash period to %ld ms", data->flash_period_ms); + syslog(LOG_INFO, "%s", log_msg); + printf("%s\n", log_msg); + free(log_msg); + } + + } else if (events[i].data.fd == btn[2]) { + if (buf[0] == '1') { + data->flash_period_ms -= 200; + if (data->flash_period_ms <= 0) { + data->flash_period_ms = 200; // Minimum period of 200ms + } + char* log_msg = malloc(100); + snprintf(log_msg, 100, "Decrease flash period to %ld ms", data->flash_period_ms); + syslog(LOG_INFO, "%s", log_msg); + printf("%s\n", log_msg); + free(log_msg); + } + } + } + } + + for (int i=0; iepoll_fd, &ev, 1, -1); + if (n == -1) { + perror("epoll_wait failed"); + break; + } + + uint64_t val; + if (read(data->timer_fd, &val, sizeof(val)) != sizeof(val)) { + perror("read timerfd failed"); + break; + } + + long time_on_ms = data->flash_period_ms / 100 * DUTY_CYCLE_PERCENT; // 2% duty + long time_off_ms = data->flash_period_ms - time_on_ms; // rest of the period + + int delay = 0; + if (isLedOn == 0) { + delay = time_on_ms; // 2% duty + led_on(led); + isLedOn = 1; + } else { + delay = time_off_ms; // rest of the period + led_off(led); + isLedOn = 0; + } + + timer_set_time(&data->timer_fd, delay); + + } + return NULL; +} + +int main(int argc, char* argv[]) { + (void)argc; + (void)argv; + ThreadData data; + pthread_t thread; + openlog("CSEL Logs", LOG_PID, LOG_USER); + syslog(LOG_INFO, "Start logging silly led-controller"); + + data.flash_period_ms = DEFAULT_TIME_MS; + + // Create timerfd + data.timer_fd = timer_create_empty(); + timer_set_time(&data.timer_fd, data.flash_period_ms); + + // Create epoll instance + data.epoll_fd = epoll_create1(0); + if (data.epoll_fd == -1) { + perror("ERROR while create epoll"); + exit(20); + } + + timer_link_to_epoll(&data.timer_fd, &data.epoll_fd); + + + if (pthread_create(&thread, NULL, timer_thread, &data) != 0) { + perror("Failed to create timer thread"); + exit(30); + } + + + // Setup button thread + pthread_t btn_thread_inst; + pthread_create(&btn_thread_inst, NULL, btn_thread, &data); + + while (1) { + sleep(1); + } + + closelog(); + return 0; +} diff --git a/src/06-mini-project/daemon/timer/timer.c b/src/06-mini-project/daemon/timer/timer.c new file mode 100644 index 0000000..18c0530 --- /dev/null +++ b/src/06-mini-project/daemon/timer/timer.c @@ -0,0 +1,51 @@ +#include "timer.h" + +#include +#include +#include +#include +#include +#include +#include + + +int timer_create_empty() { + + // Create timerfd + int timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); + if (timer_fd == -1) { + perror("timerfd_create failed"); + exit(10); + } + + return timer_fd; +} + + +void timer_set_time(int* timer_fd, long period_ms) { + // https://www.man7.org/linux/man-pages/man3/itimerspec.3type.html + struct itimerspec its; + + // Periodic interval + its.it_interval.tv_sec = 0; + its.it_interval.tv_nsec = 0; + + // Initial expiration + its.it_value.tv_sec = period_ms / 1000; + its.it_value.tv_nsec = (period_ms % 1000) * 1000000; + + if (timerfd_settime(*timer_fd, 0, &its, NULL) == -1) { + perror("timerfd_settime failed"); + exit(11); + } +} + +void timer_link_to_epoll(int* timer_fd, int* epoll_fd) { + struct epoll_event ev; + ev.events = EPOLLIN; + ev.data.fd = *timer_fd; + if (epoll_ctl(*epoll_fd, EPOLL_CTL_ADD, *timer_fd, &ev) == -1) { + perror("ERROR while add timerfd to epoll"); + exit(21); + } +} \ No newline at end of file diff --git a/src/06-mini-project/daemon/timer/timer.h b/src/06-mini-project/daemon/timer/timer.h new file mode 100644 index 0000000..7c8c8a7 --- /dev/null +++ b/src/06-mini-project/daemon/timer/timer.h @@ -0,0 +1,8 @@ +#ifndef CSEL_WORKSPACE_TIMER_H +#define CSEL_WORKSPACE_TIMER_H + +int timer_create_empty(); +void timer_set_time(int* timer_fd, long period_ms); +void timer_link_to_epoll(int* timer_fd, int* epoll_fd); + +#endif //CSEL_WORKSPACE_TIMER_H