Compare commits
10 Commits
6ef9b6a244
...
3097b3ae8d
| Author | SHA1 | Date | |
|---|---|---|---|
|
3097b3ae8d
|
|||
|
93c439abb8
|
|||
|
e46d3bb4b2
|
|||
|
16e60f46e8
|
|||
|
0f6502bf9b
|
|||
|
ca5dd30872
|
|||
|
5f1974da91
|
|||
|
d727a75357
|
|||
|
c120e0d663
|
|||
|
361b6eae4d
|
@@ -42,7 +42,7 @@
|
||||
|
||||
#v(5em)
|
||||
#infobox()[
|
||||
The repository for this labs can be found at the following address:
|
||||
The repository for these lab can be found at the following address:
|
||||
|
||||
#align(center)[https://github.com/Klagarge/MSE-MA-CSEL]
|
||||
]
|
||||
@@ -67,38 +67,40 @@ The @fig:general-architecture shows the general architecture of the project.
|
||||
|
||||
This @led and the measure of the temperature is managed by a kernel module. This module support an automatic and manual mode. In the automatic mode, the blinking frequency is automatically adjusted according to the temperature. We can switch this mode by a @sysfs entry. In the manual mode, we can set the blinking frequency by writing in another @sysfs entry. The @sysfs also provide an entry to read the current temperature and blinking frequency.
|
||||
|
||||
Another part in this mini-project is to create a daemon in user-space to control manually the fan. The buttons are read by the daemon to increase and decrease the blinking frequency in manual mode. The daemon also display the current temperature and blinking frequency on an @oled screen. The daemon can also be controlled by a @ipc interface.
|
||||
Another part in this mini-project is to create a daemon in user-space to control manually the fan. The buttons are read by the daemon to increase and decrease the blinking frequency in manual mode. The daemon also displays the current temperature and blinking frequency on an @oled screen. The daemon can also be controlled by an @ipc interface.
|
||||
|
||||
Finally, a tiny @cli is implemented to control the daemon through the @ipc interface.
|
||||
|
||||
#general-architecture
|
||||
|
||||
== Architecture
|
||||
#pagebreak()
|
||||
= Architecture
|
||||
|
||||
In our architecture, we manage to separate with callback our functionalities. Then, we use threads for multiprocessing which involve to implements some atomic operations, signals and mutex. We add socketpair and @sysfs for communication. Finally, we get some information through registers.
|
||||
In our architecture, we manage to separate with callback our functionalities. Then, we use threads for multiprocessing which involve to implement some atomic operations, signals and mutex. We add socket pair and @sysfs for communication. Finally, we get some information through registers.
|
||||
|
||||
== Kernel
|
||||
The kernel part is separated in three main part: the blink, the temperature and the @sysfs. All this part are initialize in the main but handle in a regulator that build the logic of the auto/man mode. In auto mode, the regulator sets the frequency according to the temperature. The regulator also handle the @sysfs for setting the mode and the frequency in case of the manual mode.
|
||||
The kernel part is separated in three main parts: the blink, the temperature, and the @sysfs. All this part are initialized in the main but handle in a regulator that build the logic of the auto/man mode. In auto mode, the regulator sets the frequency according to the temperature. The regulator also handles the @sysfs for setting the mode and the frequency in case of the manual mode.
|
||||
|
||||
=== blink
|
||||
The `blink.c` and `blink.h` files implement the part that control the status @led. It's a kernel module, so we have an init and an exit function. The init function create a kernel thread that blink to a specific frequency. The exit function stop this thread. The period is stored in a global `atomic_t` variable, so it can be safely set with the `adjust_period` function.
|
||||
=== temperature
|
||||
|
||||
The read of temperature is done through register. It implements the function to calculate the temperature from the register. It changes the formula when the temperature is over 70°C as specified in the datasheet.
|
||||
The read of temperature is done through the register. It implements the function to calculate the temperature from the register. It changes the formula when the temperature is over 70 °C, as specified in the datasheet.
|
||||
|
||||
=== #gls("sysfs", long: false)
|
||||
|
||||
It uses some callbacks for every actions in the module:
|
||||
It uses some callbacks for every action in the module:
|
||||
- read temperature
|
||||
- set and get mode
|
||||
- set period
|
||||
- get period
|
||||
|
||||
We separates the setter and the getter of the period to avoid some issue. Because if we set a wrong value or in automatic mode, the value would be wrong for getting it. In the way we did it, the read value will be the current.
|
||||
We separate the setter and the getter of the period to avoid some issue. Because if we set a wrong value or in automatic mode, the value would be wrong for getting it. In the way we did it, the read value will be the current.
|
||||
|
||||
#pagebreak()
|
||||
== Daemon
|
||||
|
||||
The deamon has the core in `app`. It handles the `sysfs` functions needed by the different features. It provides them for the @oled screen, buttons, @led:pl and @ipc server.
|
||||
The daemon has the core in `app`. It handles the `sysfs` functions needed by the different features. It provides them for the @oled screen, buttons, @led:pl and @ipc server.
|
||||
|
||||
=== #gls("gpio", long: false)
|
||||
We develop the @gpio part as near as possible with a pseudo class for the @led and a pseudo class for the button. The @led class is quite simple and help to have a good understanding of this principle. As shown in @fig:led-class-header, we create a structure for the @led. A `LED_init` function is used to create a @led object by returning a pointer to this structure. Function to this class start with the same prefix `LED_` and take a pointer to the structure as parameter.
|
||||
@@ -122,7 +124,7 @@ We develop the @gpio part as near as possible with a pseudo class for the @led a
|
||||
caption: "Led class header"
|
||||
) <fig:led-class-header>
|
||||
|
||||
We develop the button in the same way, with class spirit. But a button have no function to control it, but only a callback that need to be set as shown in @fig:button-class-header. So this pseudo class abstract the complexity of a button and we provide a simple @api with a nice callback system. Behind the scene, we have a thread that look the button file with an `@epoll` and call the callback when the button is pushed. The first button to be initialized create this static thread. All new button are added on the event list of this thread.
|
||||
We develop the button in the same way, with class spirit. But a button has no function to control it, but only a callback that need to be set as shown in @fig:button-class-header. So this pseudo class abstract the complexity of a button, and we provide a simple @api with a nice callback system. Behind the scene, we have a thread that looks the button file with an `@epoll` and call the callback when the button is pushed. The first button to be initialized create this static thread. All new buttons are added on the event list of this thread.
|
||||
|
||||
#figure(
|
||||
[```c
|
||||
@@ -136,10 +138,11 @@ We develop the button in the same way, with class spirit. But a button have no f
|
||||
|
||||
=== #gls("ipc", long: false)
|
||||
|
||||
The @ipc provides a server to handle messages from other processes with a socketpair. All is defined in a common file: `src/06-mini-project/common/common_ipc.h`. This file implements the action and the format of the message through the socket.
|
||||
The @ipc provides a server to handle messages from other processes with a socket pair. All is defined in a common file: `src/06-mini-project/common/common_ipc.h`. This file implements the action and the format of the message through the socket.
|
||||
|
||||
#pagebreak()
|
||||
=== #gls("oled", long: false)
|
||||
The @oled part have nothing special, we basically use the provided example. But we had to modify the devicetree to add the @i2c that control the screen. It was the first time we had to modify the buildroot part. We forgot a bit how it's absolutely not enough to modify in `/config/board/.../nanopi-neo-plus2.dts`. In the `get-buildroot.sh` script, there is a rsync command that was done only at the full beginning of the semester when we initialize everything. To effectively modify the devicetree, we had to copy our modification, then rebuild (it's short because most parts are already built):
|
||||
The @oled part has nothing special, we basically use the provided example. But we had to modify the devicetree to add the @i2c that control the screen. It was the first time we had to modify the buildroot part. We forgot a bit how it's absolutely not enough to modify in `/config/board/.../nanopi-neo-plus2.dts`. In the `get-buildroot.sh` script, there is a rsync command that was done only at the full beginning of the semester when we initialize everything. To effectively modify the devicetree, we had to copy our modification, then rebuild (it's short because most parts are already built):
|
||||
|
||||
```bash
|
||||
rsync -a /workspace/config/board/ /buildroot/board/
|
||||
@@ -154,11 +157,11 @@ Then, if we boot with @tftp, we can simply reboot. Otherwise, we have to reflash
|
||||
|
||||
=== application
|
||||
|
||||
This part is the core of the daemon and provides @api for the @oled screen, the buttons and the @ipc to set and get values from the module. It uses @sysfs technology to communicate with the kernel.
|
||||
This part is the core of the daemon and provides @api for the @oled screen, the buttons, and the @ipc to set and get values from the module. It uses @sysfs technology to communicate with the kernel.
|
||||
|
||||
It implements some specific action like increase and decrease the period of the @led. It provides too specifics functions for the buttons, because it has to signal with the power @led when it is pushed. We called that an animation.
|
||||
It implements some specific action like increase and decrease the period of the @led. It provides too specifics functions for the buttons because it has to signal with the power @led when it is pushed. We called that an animation.
|
||||
|
||||
This animation is managed by a signal and a condition. The function increase and decrease for the buttons increment a counter and send a signal to the animation thread. It handles it and make the animation until the counter reach 0. Then it waits with the `pthread_cond_wait`.
|
||||
This animation is managed by a signal and a condition. The function increase and decrease for the buttons increment a counter and send a signal to the animation thread. It handles it and makes the animation until the counter reach 0. Then it waits with the `pthread_cond_wait`.
|
||||
|
||||
|
||||
|
||||
@@ -166,15 +169,16 @@ This animation is managed by a signal and a condition. The function increase and
|
||||
|
||||
The @cli is connected to the daemon through the socketpair define in the `common` as the @ipc. It uses the same struct and actions for changing mode or set, increase, decrease a period.
|
||||
|
||||
It is installed in the `/usr/bin` by the `justfile`. It allows to use it from everywhere in the terminal. It can be use as a tool.
|
||||
It is installed in the `/usr/bin` by the `justfile`. It allows using it from everywhere in the terminal. It can be used as a tool.
|
||||
|
||||
#pagebreak()
|
||||
= Future work
|
||||
#let link-github-project = "https://github.com/users/Klagarge/projects/3/views/1?filterQuery=is%3Aopen"
|
||||
C.f.: #link(link-github-project)[Github project] #footnote(link-github-project)
|
||||
C.f.: #link(link-github-project)[GitHub project] #footnote(link-github-project)
|
||||
|
||||
|
||||
= Conclusion
|
||||
Fun, but not enough time for more over-engineering.
|
||||
Really fun, but sadly not enough time for more over-engineering. #emoji.face.cry
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -48,11 +48,11 @@ static int process_arguments(int argc, char *argv[], ipc_msg_t *msg) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else if (strcmp(argv[1], INC) == 0 && argc == 2) {
|
||||
else if (strcmp(argv[1], DEC) == 0 && argc == 2) {
|
||||
msg->command = CMD_INC_PERIOD;
|
||||
msg->value = 0; /* Ignored by daemon */
|
||||
}
|
||||
else if (strcmp(argv[1], DEC) == 0 && argc == 2) {
|
||||
else if (strcmp(argv[1], INC) == 0 && argc == 2) {
|
||||
msg->command = CMD_DEC_PERIOD;
|
||||
msg->value = 0; /* Ignored by daemon */
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
static LED* led;
|
||||
|
||||
@@ -11,10 +13,11 @@ static LED* led;
|
||||
static pthread_t anim_thread_id;
|
||||
static pthread_mutex_t anim_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t anim_condition = PTHREAD_COND_INITIALIZER; // SHARED RESOURCES
|
||||
static pthread_mutex_t app_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* The counter of pending animations */
|
||||
static int pending_animations = 0; // SHARED RESOURCES
|
||||
static int keep_running = 1;
|
||||
static atomic_bool keep_running = true;
|
||||
|
||||
void btn_set_led(LED* l) {
|
||||
if (l != NULL) {
|
||||
@@ -31,13 +34,19 @@ uint32_t get_period() {
|
||||
}
|
||||
|
||||
void increase_period() {
|
||||
pthread_mutex_lock(&app_lock);
|
||||
uint32_t current_period = sysfs_get_period();
|
||||
set_period(current_period + GAP_PERIOD_MS);
|
||||
pthread_mutex_unlock(&app_lock);
|
||||
}
|
||||
|
||||
void decrease_period() {
|
||||
pthread_mutex_lock(&app_lock);
|
||||
uint32_t current_period = sysfs_get_period();
|
||||
set_period(current_period - GAP_PERIOD_MS);
|
||||
if (current_period > GAP_PERIOD_MS) {
|
||||
set_period(current_period - GAP_PERIOD_MS);
|
||||
}
|
||||
pthread_mutex_unlock(&app_lock);
|
||||
}
|
||||
|
||||
int get_mode() {
|
||||
@@ -49,8 +58,10 @@ void set_mode(int mode) {
|
||||
}
|
||||
|
||||
void mode_toggle() {
|
||||
pthread_mutex_lock(&app_lock);
|
||||
int current_mode = sysfs_get_mode();
|
||||
set_mode(current_mode ^ 1);
|
||||
pthread_mutex_unlock(&app_lock);
|
||||
}
|
||||
|
||||
float get_temperature() {
|
||||
@@ -93,9 +104,9 @@ static void* animation_worker(void* arg) {
|
||||
/* Perform the visual task */
|
||||
if (led != NULL) {
|
||||
LED_on(led);
|
||||
usleep(150000);
|
||||
usleep(20000);
|
||||
LED_off(led);
|
||||
usleep(100000); /* Small delay between consecutive pulses */
|
||||
usleep(50000); /* Small delay between consecutive pulses */
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
#include "sysfs.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
static pthread_mutex_t sysfs_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
float sysfs_get_temperature() {
|
||||
pthread_mutex_lock(&sysfs_lock);
|
||||
FILE *f = fopen(PATH_TEMPERATURE, "r");
|
||||
if (f == NULL) {
|
||||
pthread_mutex_unlock(&sysfs_lock);
|
||||
return 0.0f; /* Return 0.0 if the kernel module is not loaded */
|
||||
}
|
||||
|
||||
@@ -19,48 +22,70 @@ float sysfs_get_temperature() {
|
||||
*/
|
||||
if (fscanf(f, "%f", &temp) == 1) {
|
||||
fclose(f);
|
||||
pthread_mutex_unlock(&sysfs_lock);
|
||||
return temp;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
pthread_mutex_unlock(&sysfs_lock);
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
uint32_t sysfs_get_mode() {
|
||||
pthread_mutex_lock(&sysfs_lock);
|
||||
FILE *f = fopen(PATH_MODE, "r");
|
||||
if (f == NULL) return 0;
|
||||
if (f == NULL) {
|
||||
pthread_mutex_unlock(&sysfs_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t mode = 0;
|
||||
fscanf(f, "%u", &mode);
|
||||
fclose(f);
|
||||
pthread_mutex_unlock(&sysfs_lock);
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
void sysfs_set_mode(uint32_t mode) {
|
||||
pthread_mutex_lock(&sysfs_lock);
|
||||
FILE *f = fopen(PATH_MODE, "w");
|
||||
if (f == NULL) return;
|
||||
if (f == NULL) {
|
||||
pthread_mutex_unlock(&sysfs_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Write the integer as an ASCII string */
|
||||
fprintf(f, "%u\n", mode);
|
||||
fclose(f);
|
||||
pthread_mutex_unlock(&sysfs_lock);
|
||||
}
|
||||
|
||||
uint32_t sysfs_get_period() {
|
||||
pthread_mutex_lock(&sysfs_lock);
|
||||
FILE *f = fopen(PATH_PERIOD_ST, "r");
|
||||
if (f == NULL) return 0;
|
||||
if (f == NULL) {
|
||||
pthread_mutex_unlock(&sysfs_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t period = 0;
|
||||
fscanf(f, "%u", &period);
|
||||
fclose(f);
|
||||
pthread_mutex_unlock(&sysfs_lock);
|
||||
|
||||
return period;
|
||||
}
|
||||
|
||||
void sysfs_set_period(uint32_t period) {
|
||||
pthread_mutex_lock(&sysfs_lock);
|
||||
FILE *f = fopen(PATH_PERIOD_SET, "w");
|
||||
if (f == NULL) return;
|
||||
if (f == NULL) {
|
||||
pthread_mutex_unlock(&sysfs_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(f, "%u\n", period);
|
||||
fclose(f);
|
||||
pthread_mutex_unlock(&sysfs_lock);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#define MAX_BTN 10
|
||||
BTN* btn_list[MAX_BTN];
|
||||
|
||||
pthread_mutex_t btn_list_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
int epoll_fd;
|
||||
struct epoll_event ev[MAX_BTN];
|
||||
atomic_int btn_tail = 0;
|
||||
@@ -27,9 +29,11 @@ void BTN_add_epoll_event(BTN* btn, int tail);
|
||||
void epoll_init();
|
||||
static void* epoll_thread(void* arg);
|
||||
|
||||
BTN* BTN_init(BTN_type type) {
|
||||
BTN* btn = malloc(sizeof(BTN));
|
||||
if (btn == NULL) return NULL;
|
||||
int BTN_init(BTN* btn, BTN_type type) {
|
||||
if (btn == NULL) return -1;
|
||||
|
||||
pthread_mutex_init(&btn->mutex, NULL);
|
||||
btn->callback = NULL;
|
||||
|
||||
char gpio_path[32] = GPIO_BTN_BASE;
|
||||
switch (type) {
|
||||
@@ -44,15 +48,18 @@ BTN* BTN_init(BTN_type type) {
|
||||
break;
|
||||
default:
|
||||
printf("Invalid button type\n");
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
strcat(gpio_path, btn->pin);
|
||||
|
||||
int f = open(GPIO_UNEXPORT, O_WRONLY);
|
||||
write(f, btn->pin, strlen(btn->pin));
|
||||
close(f);
|
||||
if (f != -1) {
|
||||
write(f, btn->pin, strlen(btn->pin));
|
||||
close(f);
|
||||
}
|
||||
|
||||
f = open(GPIO_EXPORT, O_WRONLY);
|
||||
if (f == -1) return -1;
|
||||
write(f, btn->pin, strlen(btn->pin));
|
||||
close(f);
|
||||
|
||||
@@ -61,6 +68,7 @@ BTN* BTN_init(BTN_type type) {
|
||||
strcat(direction_path, "/direction");
|
||||
|
||||
f = open(direction_path, O_WRONLY);
|
||||
if (f == -1) return -1;
|
||||
write(f, "in", 2);
|
||||
close(f);
|
||||
|
||||
@@ -69,7 +77,8 @@ BTN* BTN_init(BTN_type type) {
|
||||
strcat(edge_path, "/edge");
|
||||
|
||||
f = open(edge_path, O_WRONLY);
|
||||
write(f, "both", 4); // "both" means it triggers on press AND release
|
||||
if (f == -1) return -1;
|
||||
write(f, "both", 4);
|
||||
close(f);
|
||||
|
||||
char value_path[100];
|
||||
@@ -79,7 +88,7 @@ BTN* BTN_init(BTN_type type) {
|
||||
f = open(value_path, O_RDONLY);
|
||||
if (f == -1) {
|
||||
printf("Failed to setup button on pin %s\n", btn->pin);
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
btn->fd = f;
|
||||
|
||||
@@ -87,10 +96,13 @@ BTN* BTN_init(BTN_type type) {
|
||||
char buf[2];
|
||||
pread(btn->fd, buf, sizeof(buf), 0);
|
||||
|
||||
pthread_mutex_lock(&btn_list_mutex);
|
||||
int tail = atomic_fetch_add(&btn_tail, 1);
|
||||
if (tail >= MAX_BTN) {
|
||||
pthread_mutex_unlock(&btn_list_mutex);
|
||||
perror("Failed to add epoll event");
|
||||
exit(EXIT_FAILURE);
|
||||
close(btn->fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
btn_list[tail] = btn;
|
||||
@@ -98,22 +110,35 @@ BTN* BTN_init(BTN_type type) {
|
||||
epoll_init();
|
||||
}
|
||||
BTN_add_epoll_event(btn, tail);
|
||||
pthread_mutex_unlock(&btn_list_mutex);
|
||||
|
||||
return btn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BTN_deinit(BTN* btn) {
|
||||
if (btn == NULL) return;
|
||||
pthread_mutex_lock(&btn->mutex);
|
||||
if (btn->fd != -1) {
|
||||
close(btn->fd);
|
||||
btn->fd = -1;
|
||||
}
|
||||
pthread_mutex_unlock(&btn->mutex);
|
||||
pthread_mutex_destroy(&btn->mutex);
|
||||
}
|
||||
|
||||
void BTN_set_callback(BTN* btn, BTN_callback callback) {
|
||||
pthread_mutex_lock(&btn->mutex);
|
||||
btn->callback = callback;
|
||||
pthread_mutex_unlock(&btn->mutex);
|
||||
}
|
||||
|
||||
// TODO add mutex to protect this function
|
||||
void BTN_add_epoll_event(BTN* btn, int tail) {
|
||||
|
||||
// EPOLLIN is working well as EPOLLPRI (which is more used for priority data)
|
||||
// EPOLLERR is used to detect if there is an error
|
||||
// EPOLLET is for edge triggered mode (non-blocking)
|
||||
ev[tail].events = EPOLLIN | EPOLLERR | EPOLLET;
|
||||
ev[tail].data.fd = btn->fd;
|
||||
ev[tail].data.ptr = btn;
|
||||
|
||||
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, btn->fd, &ev[tail]);
|
||||
if (ret < 0) {
|
||||
@@ -146,28 +171,19 @@ static void* epoll_thread(void* arg) {
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
char buf[2];
|
||||
int fd = events[i].data.fd;
|
||||
int tail = -1;
|
||||
BTN* btn = NULL;
|
||||
for (int j = 0; j < MAX_BTN; j++) {
|
||||
if (btn_list[j] == NULL) continue;
|
||||
if (btn_list[j]->fd == fd) {
|
||||
tail = j;
|
||||
btn = btn_list[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tail == -1) {
|
||||
printf("No button found for fd %d\n", fd);
|
||||
continue;
|
||||
}
|
||||
pread(btn->fd, buf, sizeof(buf), 0);
|
||||
if (buf[0] == '1') {
|
||||
// printf("Button %s pressed\n", btn->pin);
|
||||
if (btn->callback != NULL) {
|
||||
btn->callback();
|
||||
BTN* btn = (BTN*)events[i].data.ptr;
|
||||
if (btn == NULL) continue;
|
||||
|
||||
pthread_mutex_lock(&btn->mutex);
|
||||
if (btn->fd != -1) {
|
||||
pread(btn->fd, buf, sizeof(buf), 0);
|
||||
if (buf[0] == '1') {
|
||||
if (btn->callback != NULL) {
|
||||
btn->callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&btn->mutex);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef BUTTON_H
|
||||
#define BUTTON_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#define GPIO_BTN_INCREASE "3"
|
||||
#define GPIO_BTN_DECREASE "0"
|
||||
#define GPIO_BTN_MODE "2"
|
||||
@@ -17,9 +19,11 @@ typedef struct {
|
||||
int fd;
|
||||
char pin[32];
|
||||
BTN_callback callback;
|
||||
pthread_mutex_t mutex;
|
||||
} BTN;
|
||||
|
||||
BTN* BTN_init(BTN_type type);
|
||||
int BTN_init(BTN* btn, BTN_type type);
|
||||
void BTN_deinit(BTN* btn);
|
||||
void BTN_set_callback(BTN* btn, BTN_callback callback);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,9 +16,8 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
LED* LED_init(LED_type type) {
|
||||
LED* led = malloc(sizeof(LED));
|
||||
if (led == NULL) return NULL;
|
||||
int LED_init(LED* led, LED_type type) {
|
||||
if (led == NULL) return -1;
|
||||
|
||||
// Concatenate GPIO LED path based on type
|
||||
char gpio_path[32] = GPIO_LED_BASE;
|
||||
@@ -32,7 +31,7 @@ LED* LED_init(LED_type type) {
|
||||
break;
|
||||
default:
|
||||
printf("Invalid LED type\n");
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
strcat(gpio_path, pin);
|
||||
|
||||
@@ -63,36 +62,54 @@ LED* LED_init(LED_type type) {
|
||||
f = open(value_path, O_RDWR);
|
||||
if (f == -1) {
|
||||
printf("Failed to setup led on pin %s\n", pin);
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
led->gpio = f;
|
||||
return led;
|
||||
if (pthread_mutex_init(&led->mutex, NULL) != 0) {
|
||||
close(f);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LED_on(LED* led) {
|
||||
if (led == NULL) {
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&led->mutex);
|
||||
pwrite(led->gpio, "1", sizeof("1"), 0);
|
||||
pthread_mutex_unlock(&led->mutex);
|
||||
}
|
||||
|
||||
void LED_off(LED* led) {
|
||||
if (led == NULL) {
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&led->mutex);
|
||||
pwrite(led->gpio, "0", sizeof("0"), 0);
|
||||
pthread_mutex_unlock(&led->mutex);
|
||||
}
|
||||
|
||||
void LED_toggle(LED* led) {
|
||||
if (led == NULL) {
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&led->mutex);
|
||||
char value[2];
|
||||
pread(led->gpio, value, sizeof(value), 0);
|
||||
if (value[0] == '0') {
|
||||
LED_on(led);
|
||||
pwrite(led->gpio, "1", sizeof("1"), 0);
|
||||
} else {
|
||||
LED_off(led);
|
||||
pwrite(led->gpio, "0", sizeof("0"), 0);
|
||||
}
|
||||
pthread_mutex_unlock(&led->mutex);
|
||||
}
|
||||
|
||||
void LED_deinit(LED* led) {
|
||||
if (led == NULL) {
|
||||
return;
|
||||
}
|
||||
pthread_mutex_destroy(&led->mutex);
|
||||
close(led->gpio);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef LED_H
|
||||
#define LED_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#define GPIO_LED_STATUS "10"
|
||||
#define GPIO_LED_POWER "362"
|
||||
|
||||
@@ -11,11 +13,13 @@ typedef enum {
|
||||
|
||||
typedef struct {
|
||||
int gpio;
|
||||
pthread_mutex_t mutex;
|
||||
} LED;
|
||||
|
||||
LED* LED_init(LED_type type);
|
||||
int LED_init(LED* led, LED_type type);
|
||||
void LED_on(LED* led);
|
||||
void LED_off(LED* led);
|
||||
void LED_toggle(LED* led);
|
||||
void LED_deinit(LED* led);
|
||||
|
||||
#endif //LED_H
|
||||
|
||||
@@ -7,17 +7,22 @@
|
||||
#include <pthread.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
/* Global variables for the IPC module */
|
||||
static int server_fd = -1;
|
||||
static pthread_t ipc_thread;
|
||||
static struct ipc_callbacks_t current_cbs = {0};
|
||||
static int is_running = 0;
|
||||
static pthread_mutex_t cb_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static atomic_bool is_running = false;
|
||||
|
||||
/**
|
||||
* process_message() - Route the received IPC message to the correct callback.
|
||||
*/
|
||||
static void process_message(ipc_msg_t *msg) {
|
||||
pthread_mutex_lock(&cb_mutex);
|
||||
switch (msg->command) {
|
||||
case CMD_SET_MODE:
|
||||
if (current_cbs.on_set_mode) {
|
||||
@@ -47,6 +52,7 @@ static void process_message(ipc_msg_t *msg) {
|
||||
printf("[IPC] Received unknown command: %d\n", msg->command);
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(&cb_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,9 +98,11 @@ int start_ipc_server(struct ipc_callbacks_t *cbs) {
|
||||
}
|
||||
|
||||
/* Save the provided callbacks */
|
||||
pthread_mutex_lock(&cb_mutex);
|
||||
if (cbs) {
|
||||
current_cbs = *cbs;
|
||||
}
|
||||
pthread_mutex_unlock(&cb_mutex);
|
||||
|
||||
/* Create local socket */
|
||||
server_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
@@ -139,7 +147,7 @@ int stop_ipc_server(void) {
|
||||
printf("[IPC] Stopping server...\n");
|
||||
|
||||
/* Signal thread to stop */
|
||||
is_running = 0;
|
||||
atomic_store(&is_running, false);
|
||||
|
||||
/* Unblock the recvfrom() by shutting down the socket */
|
||||
if (server_fd != -1) {
|
||||
@@ -155,7 +163,9 @@ int stop_ipc_server(void) {
|
||||
unlink(SOCKET_PATH);
|
||||
|
||||
/* Clear callbacks */
|
||||
pthread_mutex_lock(&cb_mutex);
|
||||
memset(¤t_cbs, 0, sizeof(struct ipc_callbacks_t));
|
||||
pthread_mutex_unlock(&cb_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
@build:
|
||||
make
|
||||
|
||||
@install:
|
||||
install -m 0755 daemon /rootfs/usr/bin/daemon
|
||||
echo "daemon installed successfully in /rootfs/usr/bin/"
|
||||
|
||||
@clean:
|
||||
make clean
|
||||
rm -f -- daemon
|
||||
rm -f /rootfs/usr/bin/daemon
|
||||
|
||||
@@ -21,17 +21,30 @@
|
||||
|
||||
|
||||
int main(void) {
|
||||
BTN btn_inc, btn_dec, btn_mode;
|
||||
|
||||
if (BTN_init(&btn_inc, BTN_INCREASE) != 0) {
|
||||
fprintf(stderr, "Failed to initialize increase button\n");
|
||||
return 1;
|
||||
}
|
||||
if (BTN_init(&btn_dec, BTN_DECREASE) != 0) {
|
||||
fprintf(stderr, "Failed to initialize decrease button\n");
|
||||
return 1;
|
||||
}
|
||||
if (BTN_init(&btn_mode, BTN_MODE) != 0) {
|
||||
fprintf(stderr, "Failed to initialize mode button\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
BTN* btn_inc = BTN_init(BTN_INCREASE);
|
||||
BTN* btn_dec = BTN_init(BTN_DECREASE);
|
||||
BTN* btn_mode = BTN_init(BTN_MODE);
|
||||
LED led_power;
|
||||
if (LED_init(&led_power, LED_POWER) != 0) {
|
||||
fprintf(stderr, "Failed to initialize power LED\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
LED* led_power = LED_init(LED_POWER);
|
||||
|
||||
BTN_set_callback(btn_inc, btn_increase_period);
|
||||
BTN_set_callback(btn_dec, btn_decrease_period);
|
||||
BTN_set_callback(btn_mode, mode_toggle);
|
||||
BTN_set_callback(&btn_inc, btn_increase_period);
|
||||
BTN_set_callback(&btn_dec, btn_decrease_period);
|
||||
BTN_set_callback(&btn_mode, mode_toggle);
|
||||
|
||||
struct ipc_callbacks_t ipc_cbs = {
|
||||
.on_dec_period = decrease_period,
|
||||
@@ -54,7 +67,7 @@ int main(void) {
|
||||
|
||||
init_oled(&oled_cbs);
|
||||
|
||||
btn_set_led(led_power);
|
||||
btn_set_led(&led_power);
|
||||
init_animations();
|
||||
|
||||
while (1) {
|
||||
@@ -62,5 +75,9 @@ int main(void) {
|
||||
}
|
||||
|
||||
stop_ipc_server();
|
||||
LED_deinit(&led_power);
|
||||
BTN_deinit(&btn_inc);
|
||||
BTN_deinit(&btn_dec);
|
||||
BTN_deinit(&btn_mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
#include "timer.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#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
|
||||
@@ -2,21 +2,39 @@ mod kernel
|
||||
mod daemon
|
||||
mod cli
|
||||
|
||||
target_ssh := "root@192.168.53.14"
|
||||
|
||||
# List available recipes
|
||||
@default:
|
||||
just --list
|
||||
|
||||
# Build all components (kernel module, daemon, and cli)
|
||||
@build:
|
||||
just kernel::build
|
||||
just daemon::build
|
||||
just cli::build
|
||||
|
||||
# Install all components to the local rootfs
|
||||
@install:
|
||||
just kernel::install
|
||||
just daemon::install
|
||||
just cli::install
|
||||
|
||||
# Load the kernel module and start the daemon on the target
|
||||
@run:
|
||||
ssh {{target_ssh}} "modprobe temp_regulator"
|
||||
ssh {{target_ssh}} "start-stop-daemon --start --background --exec /usr/bin/daemon"
|
||||
|
||||
# Stop the daemon and unload the kernel module on the target
|
||||
@stop:
|
||||
ssh {{target_ssh}} "start-stop-daemon --stop --exec /usr/bin/daemon || true"
|
||||
ssh {{target_ssh}} "modprobe -r temp_regulator || true"
|
||||
|
||||
# Clean build artifacts and remove installed files from local rootfs
|
||||
@clean:
|
||||
just kernel::clean
|
||||
just daemon::clean
|
||||
just cli::clean
|
||||
|
||||
@all: clean build install
|
||||
# Clean, build, install, and run everything
|
||||
@all: clean build install run
|
||||
|
||||
@@ -30,10 +30,12 @@ all:
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(KDIR) M=$(PWD) clean
|
||||
rm -f $(MODPATH)/etc/modprobe.d/$(MODULE).conf
|
||||
rm -f $(MODPATH)/lib/modules/*/extra/$(MODULE).ko
|
||||
echo $(PATH)
|
||||
|
||||
install:
|
||||
$(MAKE) -C $(KDIR) M=$(PWD) INSTALL_MOD_PATH=$(MODPATH) modules_install
|
||||
# install -D -m 0644 $(MODULE).conf $(MODPATH)/etc/modprobe.d/$(MODULE).conf
|
||||
install -D -m 0644 $(MODULE).conf $(MODPATH)/etc/modprobe.d/$(MODULE).conf
|
||||
|
||||
endif
|
||||
|
||||
@@ -8,4 +8,4 @@
|
||||
make install
|
||||
|
||||
@clean:
|
||||
make clean
|
||||
make clean
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include "regulator.h"
|
||||
#include "../sysfs/sysfs.h"
|
||||
@@ -14,6 +15,7 @@ static uint32_t current_period = 1000; /* Current blinking period in ms */
|
||||
static struct task_struct *regulator_thread = NULL;
|
||||
|
||||
static struct regulator_callbacks regulator_cbs = {0};
|
||||
static DEFINE_MUTEX(regulator_lock);
|
||||
|
||||
/* --- Sysfs Callbacks --- */
|
||||
|
||||
@@ -22,22 +24,33 @@ static uint32_t cb_get_temperature(void) {
|
||||
}
|
||||
|
||||
static int cb_get_mode(void) {
|
||||
return current_mode;
|
||||
int mode;
|
||||
mutex_lock(®ulator_lock);
|
||||
mode = current_mode;
|
||||
mutex_unlock(®ulator_lock);
|
||||
return mode;
|
||||
}
|
||||
|
||||
static void cb_set_mode(int mode) {
|
||||
/* Accept only 0 or 1 as valid modes */
|
||||
if (mode == 0 || mode == 1) {
|
||||
mutex_lock(®ulator_lock);
|
||||
current_mode = mode;
|
||||
mutex_unlock(®ulator_lock);
|
||||
pr_info("regulator: Mode switched to %s\n", mode ? "Auto" : "Manual");
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t cb_get_period(void) {
|
||||
return current_period;
|
||||
uint32_t period;
|
||||
mutex_lock(®ulator_lock);
|
||||
period = current_period;
|
||||
mutex_unlock(®ulator_lock);
|
||||
return period;
|
||||
}
|
||||
|
||||
static void cb_set_period(uint32_t period_ms) {
|
||||
mutex_lock(®ulator_lock);
|
||||
/* Allow period changes only in Manual mode */
|
||||
if (current_mode == 0) {
|
||||
if (period_ms > 0) {
|
||||
@@ -51,6 +64,7 @@ static void cb_set_period(uint32_t period_ms) {
|
||||
} else {
|
||||
pr_warn("regulator: Cannot set period manually in auto mode\n");
|
||||
}
|
||||
mutex_unlock(®ulator_lock);
|
||||
}
|
||||
|
||||
/* Pack callbacks into the structure expected by sysfs */
|
||||
@@ -80,6 +94,7 @@ static void process_auto_mode(void) {
|
||||
new_period = 50;
|
||||
}
|
||||
|
||||
mutex_lock(®ulator_lock);
|
||||
/* Apply only if the period has changed to avoid unnecessary hardware updates */
|
||||
if (new_period != current_period) {
|
||||
current_period = new_period;
|
||||
@@ -87,12 +102,19 @@ static void process_auto_mode(void) {
|
||||
pr_info("regulator: Auto mode adjusted period to %u ms (Temp: %u C)\n",
|
||||
current_period, temp_c);
|
||||
}
|
||||
mutex_unlock(®ulator_lock);
|
||||
}
|
||||
|
||||
/* Background thread checking the temperature periodically */
|
||||
static int regulator_thread_fn(void *data) {
|
||||
while (!kthread_should_stop()) {
|
||||
if (current_mode == 1) {
|
||||
int mode;
|
||||
|
||||
mutex_lock(®ulator_lock);
|
||||
mode = current_mode;
|
||||
mutex_unlock(®ulator_lock);
|
||||
|
||||
if (mode == 1) {
|
||||
process_auto_mode();
|
||||
}
|
||||
msleep(1000);
|
||||
@@ -125,9 +147,6 @@ int regulator_init(struct regulator_callbacks *cbs) {
|
||||
return PTR_ERR(regulator_thread);
|
||||
}
|
||||
|
||||
/* Set initial hardware state */
|
||||
regulator_cbs.adjust_period(current_period);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include "sysfs.h"
|
||||
|
||||
@@ -13,14 +14,17 @@ static struct device *sysfs_device = NULL;
|
||||
|
||||
/* Global static structure to hold the registered callbacks */
|
||||
static struct sysfs_callbacks device_cbs = {0};
|
||||
static DEFINE_MUTEX(sysfs_lock);
|
||||
|
||||
/* Callback triggered on sysfs temperature file read */
|
||||
static ssize_t temperature_show(struct device *dev, struct device_attribute *attr, char *buf) {
|
||||
uint32_t temp = 0;
|
||||
|
||||
mutex_lock(&sysfs_lock);
|
||||
if (device_cbs.get_temperature) {
|
||||
temp = device_cbs.get_temperature();
|
||||
}
|
||||
mutex_unlock(&sysfs_lock);
|
||||
|
||||
/* Format the temperature and write it to the buffer */
|
||||
return snprintf(buf, PAGE_SIZE, "%u.%03u\n", temp / 1000, temp % 1000);
|
||||
@@ -30,9 +34,11 @@ static ssize_t temperature_show(struct device *dev, struct device_attribute *att
|
||||
static ssize_t mode_show(struct device *dev, struct device_attribute *attr, char *buf) {
|
||||
int mode = 0;
|
||||
|
||||
mutex_lock(&sysfs_lock);
|
||||
if (device_cbs.get_mode) {
|
||||
mode = device_cbs.get_mode();
|
||||
}
|
||||
mutex_unlock(&sysfs_lock);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", mode);
|
||||
}
|
||||
@@ -43,9 +49,11 @@ static ssize_t mode_store(struct device *dev, struct device_attribute *attr, con
|
||||
|
||||
/* Safely convert string from user space to integer */
|
||||
if (kstrtoint(buf, 10, &mode) == 0) {
|
||||
mutex_lock(&sysfs_lock);
|
||||
if (device_cbs.set_mode) {
|
||||
device_cbs.set_mode(mode);
|
||||
}
|
||||
mutex_unlock(&sysfs_lock);
|
||||
}
|
||||
|
||||
return count;
|
||||
@@ -55,9 +63,11 @@ static ssize_t mode_store(struct device *dev, struct device_attribute *attr, con
|
||||
static ssize_t period_status_show(struct device *dev, struct device_attribute *attr, char *buf) {
|
||||
uint32_t period = 0;
|
||||
|
||||
mutex_lock(&sysfs_lock);
|
||||
if (device_cbs.get_period) {
|
||||
period = device_cbs.get_period();
|
||||
}
|
||||
mutex_unlock(&sysfs_lock);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", period);
|
||||
}
|
||||
@@ -67,9 +77,11 @@ static ssize_t period_set_store(struct device *dev, struct device_attribute *att
|
||||
uint32_t period;
|
||||
|
||||
if (kstrtouint(buf, 10, &period) == 0) {
|
||||
mutex_lock(&sysfs_lock);
|
||||
if (device_cbs.set_period) {
|
||||
device_cbs.set_period(period);
|
||||
}
|
||||
mutex_unlock(&sysfs_lock);
|
||||
}
|
||||
|
||||
return count;
|
||||
@@ -97,10 +109,12 @@ static const struct attribute_group regulator_group = {
|
||||
int temp_regulator_sysfs_init(struct sysfs_callbacks *cbs) {
|
||||
int ret;
|
||||
|
||||
mutex_lock(&sysfs_lock);
|
||||
/* Save the callbacks provided by the main module */
|
||||
if (cbs) {
|
||||
device_cbs = *cbs;
|
||||
}
|
||||
mutex_unlock(&sysfs_lock);
|
||||
|
||||
/* Create sysfs class */
|
||||
sysfs_class = class_create(THIS_MODULE, "temp_regulator");
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include "temp.h"
|
||||
|
||||
@@ -13,8 +14,11 @@
|
||||
|
||||
static struct resource *temp_res = NULL;
|
||||
static void __iomem *temp_reg = NULL;
|
||||
static DEFINE_MUTEX(temp_lock);
|
||||
|
||||
int temp_init(void) {
|
||||
mutex_lock(&temp_lock);
|
||||
|
||||
/* Request physical memory region */
|
||||
temp_res = request_mem_region(TEMPERATURE_SENSOR_BASE_ADDR,
|
||||
TEMPERATURE_SENSOR_REG_SIZE,
|
||||
@@ -28,12 +32,17 @@ int temp_init(void) {
|
||||
if (temp_reg == NULL) {
|
||||
pr_err("temp_regulator: Failed to ioremap registers\n");
|
||||
/* Clean up previously requested region */
|
||||
release_mem_region(TEMPERATURE_SENSOR_BASE_ADDR, TEMPERATURE_SENSOR_REG_SIZE);
|
||||
temp_res = NULL;
|
||||
if (temp_res) {
|
||||
release_mem_region(TEMPERATURE_SENSOR_BASE_ADDR, TEMPERATURE_SENSOR_REG_SIZE);
|
||||
temp_res = NULL;
|
||||
}
|
||||
mutex_unlock(&temp_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pr_info("temp_regulator: Temperature sensor memory successfully mapped\n");
|
||||
|
||||
mutex_unlock(&temp_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -41,8 +50,11 @@ uint32_t read_temp(void) {
|
||||
uint32_t temperature = 0;
|
||||
uint32_t raw_val = 0;
|
||||
|
||||
mutex_lock(&temp_lock);
|
||||
|
||||
if (temp_reg == NULL) {
|
||||
pr_warn("temp_regulator: Cannot read temperature, sensor not initialized\n");
|
||||
mutex_unlock(&temp_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -58,10 +70,13 @@ uint32_t read_temp(void) {
|
||||
temperature = -1452 * (int32_t)raw_val / 10 + 259000;
|
||||
}
|
||||
|
||||
mutex_unlock(&temp_lock);
|
||||
return temperature;
|
||||
}
|
||||
|
||||
void temp_exit(void) {
|
||||
mutex_lock(&temp_lock);
|
||||
|
||||
/* Unmap virtual address space */
|
||||
if (temp_reg != NULL) {
|
||||
iounmap(temp_reg);
|
||||
@@ -74,5 +89,7 @@ void temp_exit(void) {
|
||||
temp_res = NULL;
|
||||
}
|
||||
|
||||
mutex_unlock(&temp_lock);
|
||||
|
||||
pr_info("temp_regulator: Temperature sensor resources released\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user