Compare commits
6 Commits
3097b3ae8d
...
feat/timer
| Author | SHA1 | Date | |
|---|---|---|---|
|
9fbfac5fe1
|
|||
|
22f923bfbe
|
|||
|
2d26f30adf
|
|||
|
8561386973
|
|||
|
5592c9c0fe
|
|||
|
ca8085ce09
|
@@ -14,6 +14,7 @@ RUN apt-get update && \
|
||||
device-tree-compiler \
|
||||
file \
|
||||
flex \
|
||||
just \
|
||||
libfl-dev \
|
||||
libglib2.0-dev \
|
||||
libssl-dev \
|
||||
@@ -27,7 +28,5 @@ RUN apt-get update && \
|
||||
wget \
|
||||
bear
|
||||
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin
|
||||
|
||||
COPY scripts/* /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/*
|
||||
|
||||
2
.gitattributes
vendored
@@ -1,2 +0,0 @@
|
||||
src/05-optimization/ex03/access_log_NASA_Jul95 filter=lfs diff=lfs merge=lfs -text
|
||||
src/05-optimization/ex03/access_log_NASA_Jul95_samples filter=lfs diff=lfs merge=lfs -text
|
||||
16
.gitignore
vendored
@@ -58,19 +58,3 @@ doc/**/*.pdf
|
||||
|
||||
build
|
||||
src/03-led-controller/led-controller
|
||||
src/04-multiprocessing/multiprocessing
|
||||
src/04-multiprocessing/cgroups
|
||||
src/04-multiprocessing/max-cpu
|
||||
src/05-optimization/ex01/basic
|
||||
src/05-optimization/ex01/optimized
|
||||
src/05-optimization/ex02/optimized
|
||||
src/05-optimization/ex02/basic
|
||||
|
||||
src/05-optimization/ex03/ApacheAccessLogAnalyzer.d
|
||||
src/05-optimization/ex03/ApacheAccessLogAnalyzer.o
|
||||
src/05-optimization/ex03/HostCounter.d
|
||||
src/05-optimization/ex03/HostCounter.o
|
||||
src/05-optimization/ex03/main.d
|
||||
src/05-optimization/ex03/main.d
|
||||
src/05-optimization/ex03/perf.data
|
||||
src/05-optimization/ex03/read-apache-logs
|
||||
29
README.md
@@ -37,32 +37,3 @@ sync-images.sh
|
||||
```
|
||||
|
||||
You can now "burn" the Compact Flash using [BalenaEtcher](https://www.balena.io/etcher/)
|
||||
|
||||
## Changing boot env
|
||||
|
||||
### CIFS
|
||||
|
||||
In the bootloader:
|
||||
```bash
|
||||
setenv boot_scripts boot.cifs
|
||||
saveenv
|
||||
boot
|
||||
```
|
||||
|
||||
If the workspace isn't mount control if there is the line in `/etc/fstab`:
|
||||
```text
|
||||
//192.168.53.4/workspace /workspace cifs vers=1.0,username=root,password=toor,port=1445,noserverino
|
||||
```
|
||||
|
||||
If the line isn't there, add it and mount:
|
||||
```bash
|
||||
mount -a
|
||||
```
|
||||
|
||||
### NET
|
||||
In the bootloader:
|
||||
```bash
|
||||
setenv boot_scripts boot.net
|
||||
saveenv
|
||||
boot
|
||||
```
|
||||
|
||||
@@ -5,7 +5,3 @@
|
||||
/ {
|
||||
/delete-node/ leds;
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
status = "okay";
|
||||
};
|
||||
@@ -35,10 +35,9 @@
|
||||
date: date,
|
||||
tableof: tableof,
|
||||
)
|
||||
#show link: set text(fill: blue.darken(60%))
|
||||
#v(5em)
|
||||
#infobox()[
|
||||
The repository for these labs can be found at the following address:
|
||||
The repository for this labs can be found at the following address:
|
||||
|
||||
#align(center)[https://github.com/Klagarge/MSE-MA-CSEL]
|
||||
]
|
||||
@@ -46,11 +45,22 @@
|
||||
|
||||
//-------------------------------------
|
||||
// Content
|
||||
//
|
||||
//
|
||||
|
||||
#include "lab03-silly_led/main.typ"
|
||||
#include "lab04-multiprocessing/main.typ"
|
||||
#include "lab05-optimization/main.typ"
|
||||
|
||||
#lorem(150)
|
||||
|
||||
#lorem(50)
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
#pagebreak()
|
||||
= Linux System Optimisation
|
||||
|
||||
#lorem(150)
|
||||
|
||||
#lorem(50)
|
||||
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
@@ -1,49 +1,3 @@
|
||||
#import "/doc/metadata.typ": *
|
||||
|
||||
= Linux System Programming
|
||||
|
||||
This laboratory implements a user-space application for the NanoPi NEO Plus2 that controls the blinking frequency of the status #gls("led", long: false) using three push buttons. The main goal was to replace a #gls("cpu", long: false)-intensive busy loop with an event-driven design.
|
||||
|
||||
|
||||
== Design
|
||||
The application is based on multithreading: one thread handles the #gls("led", long: false) timing, while another handles button events. #gls("gpio", long: false) are accessed through #gls("sysfs", long: false), which allows the #gls("led", long: false) and buttons to be managed as file descriptors. A key design choice was to centralize all events with a single #gls("epoll", long: false) instance, so both timer events and button events can be processed efficiently.
|
||||
|
||||
The timer thread uses only one timer and sets the initial time on every cycle. This allows us to allocate resources only once for the timer and avoid memory fragmentation. The button thread writes the next sleep duration to a shared variable, which the timer thread reads to set its next sleep interval. Since there is only one writer for this variable, we do not need a mutex to protect it.
|
||||
|
||||
|
||||
All logs are written to #gls("syslog", long: false) at the INFO level:
|
||||
```c
|
||||
// First, we open the syslog with a specific name and facility
|
||||
// LOG_PID to include the PID (process ID) in the logs
|
||||
// LOG_USER to specify the log facility (what type of program)
|
||||
openlog("CSEL Logs", LOG_PID, LOG_USER);
|
||||
|
||||
// Then log what you want:
|
||||
syslog(LOG_INFO, "Start logging silly led-controller"); // INFO level
|
||||
```
|
||||
|
||||
== Difficulties
|
||||
The most difficult part was understanding the #gls("gpio", long: false) mapping between the physical pins and the #gls("sysfs", long: false) #gls("gpio", long: false) numbers. This mapping can be found in the #link("https://linux-sunxi.org/GPIO", [*sunxi driver*]) documentation, which describes the driver for the #gls("gpio", long: false) controller.
|
||||
|
||||
== Results
|
||||
We demonstrate that the application works more efficiently than the provided silly #gls("led", long: false) controller:
|
||||
|
||||
#table(
|
||||
columns: (1fr, 1fr),
|
||||
align: center + horizon,
|
||||
stroke: none,
|
||||
[
|
||||
#figure(
|
||||
image("test-silly.png", height: 10em),
|
||||
caption:[Running the silly #gls("led", long: false) controller on the NanoPi]
|
||||
)<fig-silly>
|
||||
|
||||
],[
|
||||
#figure(
|
||||
image("test-epoll.png", height: 10em),
|
||||
caption:[Running the #gls("epoll", long: false)-based #gls("led", long: false) controller on the NanoPi]
|
||||
)<fig-epoll>
|
||||
]
|
||||
)
|
||||
|
||||
We see in @fig-silly, the silly #gls("led", long: false) controller use 100% of the #gls("cpu", long: false) in @fig-epoll we save CPU resources.
|
||||
= Linux System Programming
|
||||
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 31 KiB |
@@ -1,279 +0,0 @@
|
||||
#import "/doc/metadata.typ": *
|
||||
|
||||
= Multiprocessing
|
||||
|
||||
== Process, signals, and communication
|
||||
|
||||
The aim of this laboratory is to create a child process from a parent process using `fork()`. Both processes then execute the same code until they are terminated. This is similar to parallel programming with #gls("gpu", long: false) using #gls("cuda", long: false) or #gls("openmp", long: false). The processes are differentiated by their #gls("pid", long: false).
|
||||
|
||||
The child process must communicate with the parent process using a `socketpair`:
|
||||
```c
|
||||
/* Setup socket for inter-process communication */
|
||||
int fd[2];
|
||||
int err = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
|
||||
if (err == -1) {
|
||||
perror("socketpair fail");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
```
|
||||
This creates a local UNIX socket pair for inter-process communication. It returns two file descriptors for bidirectional communication.
|
||||
|
||||
The program must handle several signals and print their names when received:
|
||||
```c
|
||||
static void catch_signal(int signal) {
|
||||
|
||||
switch (signal) {
|
||||
case SIGHUP:
|
||||
printf("SIGHUP received\n");
|
||||
break;
|
||||
case SIGINT:
|
||||
printf("SIGINT received\n");
|
||||
exit(EXIT_SUCCESS); // to avoid to be blocked and kill it with ctrl+c
|
||||
break;
|
||||
case SIGQUIT:
|
||||
printf("SIGQUIT received\n");
|
||||
break;
|
||||
case SIGTERM:
|
||||
printf("SIGTERM received\n");
|
||||
break;
|
||||
case SIGABRT:
|
||||
printf("SIGABRT received\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#pagebreak()
|
||||
|
||||
```c
|
||||
static void install_catch_signal()
|
||||
{
|
||||
struct sigaction act = {
|
||||
.sa_handler = catch_signal,
|
||||
};
|
||||
sigemptyset(&act.sa_mask);
|
||||
sigaction(SIGHUP, &act, 0);
|
||||
sigaction(SIGINT, &act, 0);
|
||||
sigaction(SIGQUIT, &act, 0);
|
||||
sigaction(SIGTERM, &act, 0);
|
||||
sigaction(SIGABRT, &act, 0);
|
||||
}
|
||||
```
|
||||
|
||||
One important design consideration to anticipate was signal handling behaviour. If `Ctrl+C` (SIGINT) is caught, but the handler does not terminate the process, the application would continue to run and block the terminal. In that case, the only way to kill the process would be to open another terminal and use a tool like `top` or `htop`.
|
||||
|
||||
Finally, each process is pinned to its own CPU core. This is configured using `sched_setaffinity`:
|
||||
```c
|
||||
/* Setup CPU affinity for process */
|
||||
CPU_SET(child_cpu, &set);
|
||||
int ret = sched_setaffinity(parent_pid, sizeof(set), &set);
|
||||
if (ret == -1) {
|
||||
perror("sched_setaffinity");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
```
|
||||
|
||||
This can be verified by executing the program and observing CPU usage in `htop`.
|
||||
|
||||
```bash
|
||||
$ ./multiprocessing
|
||||
Child process: pid=273
|
||||
Parent process: pid=274
|
||||
Message 0: Hallo, hallo !
|
||||
Message 1: ça geht !
|
||||
Message 2: Comment vont les olives ?
|
||||
Message 3: Sacré trucs tes trucs là.
|
||||
Message 4: Ta où les vaches !!!!!
|
||||
SIGHUP received
|
||||
SIGQUIT received
|
||||
SIGTERM received
|
||||
SIGABRT received
|
||||
SIGINT received
|
||||
|
||||
```
|
||||
#figure(
|
||||
image("control_cpu_process_ex_1.png"),
|
||||
caption: [Execution of the multiprocessing program]
|
||||
)<multiprocessus>
|
||||
|
||||
|
||||
The @multiprocessus shows the #gls("pid", long: false) and the assigned CPU core for each process, which can be compared with the console output shown above.
|
||||
The child process has PID 273 and runs on core 0, whereas the parent process has PID 274 and runs on core 1.
|
||||
|
||||
== #glspl("cgroup", long: false) memory
|
||||
|
||||
The goal of this part is to understand how to use #glspl("cgroup", long: false) to limit the resources of a process. We will initially focus on memory, but #glspl("cgroup", long: false) can also be used to limit #gls("cpu", long: false), #gls("io", long: false), and other resources.
|
||||
|
||||
To limit the memory usage of a process, we can use the `memory` subsystem of #glspl("cgroup", long: false). On this NanoPi, we use #glspl("cgroup", long: false) v1.
|
||||
|
||||
We must first mount a temporary file system for #glspl("cgroup", long: false):
|
||||
```bash
|
||||
|> mount -t tmpfs none /sys/fs/cgroup
|
||||
```
|
||||
|
||||
We can then create a directory for the memory subsystem, mount the corresponding #glspl("cgroup", long: false) file system, and create a subdirectory for our specific group:
|
||||
|
||||
```bash
|
||||
# Create a directory for the memory cgroup
|
||||
|> mkdir /sys/fs/cgroup/memory
|
||||
|
||||
# Mount the cgroup filesystem with memory
|
||||
|> mount -t cgroup -o memory cgroup /sys/fs/cgroup/memory
|
||||
|
||||
# Create a subdirectory for the memory cgroup
|
||||
|> mkdir /sys/fs/cgroup/memory/0
|
||||
```
|
||||
|
||||
We can then add the current process to this memory #gls("cgroup", long: false) and set a memory limit of 20 #gls("mib", long: false):
|
||||
|
||||
```bash
|
||||
# Add the current process to the memory cgroup
|
||||
|> echo $$ > /sys/fs/cgroup/memory/0/tasks
|
||||
|
||||
# Set the memory limit to 20 MiB
|
||||
|> echo 20M > /sys/fs/cgroup/memory/0/memory.limit_in_bytes
|
||||
```
|
||||
|
||||
We can then run our test program that allocates memory in a loop to see what happens when we exceed the memory limit.
|
||||
|
||||
```c
|
||||
for (i = 0; i < NUM_BLOCKS; i++) {
|
||||
|
||||
// Allocate a block of memory
|
||||
blocks[i] = malloc(BLOCK_SIZE);
|
||||
|
||||
// [...]
|
||||
// check if failed and error, clean and exit
|
||||
|
||||
// Touch the memory to ensure it's actually allocated
|
||||
memset(blocks[i], 0, BLOCK_SIZE);
|
||||
}
|
||||
```
|
||||
|
||||
We can use the `cgroups.sh` script in `04-multiprocessing` to set up #glspl("cgroup", long: false) and run the test program. However, to execute the script in the context of our current shell, we must source it using the `.` command:
|
||||
|
||||
```bash
|
||||
|> just cgroups # Build the test program
|
||||
|> . cgroups.sh # Run the script in the current shell
|
||||
|> ./cgroups # Run the test program that allocates memory in a loop
|
||||
```
|
||||
|
||||
=== What is the behaviour of the command `echo $$ > ...` on #glspl("cgroup", long: false)?
|
||||
|
||||
The `$$` shell variable represents the #gls("pid", long: false) of the current shell. When we execute the command `echo $$ > /sys/fs/cgroup/memory/0/tasks`, we write the PID of the current shell process into the `tasks` file of the specified cgroup. This action assigns the process to that control group, meaning that any program run from this shell will inherit the resource limits and policies defined for that cgroup.
|
||||
|
||||
|
||||
=== What is the behaviour of the memory subsystem when the memory quota is exhausted? Can we modify it? If yes, how?
|
||||
|
||||
On this NanoPi, we use #glspl("cgroup", long: false) v1, so the resource configuration is done via the `memory.limit_in_bytes` file. When a process within a #gls("cgroup", long: false) exceeds the memory limit defined by this file, the Linux kernel will attempt to reclaim memory. If it cannot reclaim sufficient memory, it will invoke the #gls("oom", long: false) killer to terminate processes within that #gls("cgroup", long: false) to free up memory.
|
||||
|
||||
It is possible to modify this behaviour in several ways:
|
||||
|
||||
+ *Use "Soft Limits" (specific to #glspl("cgroup", long: false) v1):*
|
||||
In addition to a hard limit (`memory.limit_in_bytes`), a soft limit can be set via `memory.soft_limit_in_bytes`.
|
||||
*Behaviour:* The kernel does not kill the process when the soft limit is exceeded, unless the entire system runs low on memory. If global memory is low, the kernel begins reclaiming memory from cgroups that exceed their soft limits.
|
||||
|
||||
+ *Adjust the #gls("oom", long: false) Killer priority score:*
|
||||
We can specify an #gls("oom", long: false) score adjustment for the process. By modifying the `/proc/[PID]/oom_score_adj` file to the value `-1000`, the process becomes virtually immune to the #gls("oom", long: false) killer.
|
||||
|
||||
=== How to watch the memory usage?
|
||||
|
||||
We can monitor the memory usage of a control group by reading directly from its configuration files:
|
||||
|
||||
```bash
|
||||
# Current memory usage in bytes
|
||||
|> cat /sys/fs/cgroup/memory/0/memory.usage_in_bytes
|
||||
212992
|
||||
|
||||
# Maximum memory usage in bytes
|
||||
|> cat /sys/fs/cgroup/memory/0/memory.max_usage_in_bytes
|
||||
20971520
|
||||
```
|
||||
|
||||
== #glspl("cgroup", long: false) CPU
|
||||
To check this part, we need a tiny program that consumes #gls("cpu", long: false) with at least two processes.
|
||||
The following program creates a child process that performs #gls("cpu", long: false)-intensive work, while the parent process also performs #gls("cpu", long: false)-intensive work. We can then use #glspl("cgroup", long: false) to limit the #gls("cpu", long: false) usage of one of the processes and observe the effect.
|
||||
```c
|
||||
int main() {
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0) {
|
||||
cpu_intensive_work("Child process");
|
||||
exit(0);
|
||||
} else {
|
||||
cpu_intensive_work("Parent process");
|
||||
wait(NULL);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Based on the previous exercise, we should already have mounted the #glspl("cgroup", long: false) file system.
|
||||
```bash
|
||||
|> mount -t tmpfs none /sys/fs/cgroup
|
||||
```
|
||||
|
||||
We can then create and mount the #glspl("cgroup", long: false) file system for the `cpuset` subsystem:
|
||||
```bash
|
||||
# Create a directory for the cpuset cgroup
|
||||
|> mkdir /sys/fs/cgroup/cpuset
|
||||
|
||||
# Mount the cgroup filesystem with cpuset
|
||||
|> mount -t cgroup -o cpu,cpuset cpuset /sys/fs/cgroup/cpuset
|
||||
```
|
||||
|
||||
With these prerequisites met, we can create two groups, one for each instance of our running program. Using the commands below, we assign one or more #gls("cpu", long: false) cores to each group via `cpuset.cpus`. I'm not sure about the `cpuset.mems` file, but it seems to be related to memory nodes. It's definitely a topic that should be explored more in depth, but for now, we set to `0` as specified in the lab instructions:
|
||||
|
||||
```bash
|
||||
# Create and allocate CPU for program "low"
|
||||
|> mkdir /sys/fs/cgroup/cpuset/low
|
||||
|> echo 1 > /sys/fs/cgroup/cpuset/low/cpuset.cpus
|
||||
|> echo 0 > /sys/fs/cgroup/cpuset/low/cpuset.mems
|
||||
```
|
||||
|
||||
#pagebreak()
|
||||
|
||||
```bash
|
||||
# Create and allocate CPU for program "high"
|
||||
|> mkdir /sys/fs/cgroup/cpuset/high
|
||||
|> echo 2,3 > /sys/fs/cgroup/cpuset/high/cpuset.cpus
|
||||
|> echo 0 > /sys/fs/cgroup/cpuset/high/cpuset.mems
|
||||
```
|
||||
|
||||
We can then open two shells and run the test program in each of them, while adding each program to its corresponding control group:
|
||||
```bash
|
||||
# In the first shell, add it to the "low" cgroup and run the test program
|
||||
|> . ./max-cpu.sh low
|
||||
|
||||
# In the second shell, add it to the "high" cgroup and run the test program
|
||||
|> . ./max-cpu.sh high
|
||||
```
|
||||
|
||||
As shown in @max-cpu, as expected, both processes in the "low" program are limited to #gls("cpu", long: false) core 1, while the "high" program uses #gls("cpu", long: false) cores 2 and 3 (one for each process).
|
||||
|
||||
#figure(
|
||||
image("max-cpu.png", width: 90%),
|
||||
caption: [CPU usage of the two programs with dedicated resources]
|
||||
)<max-cpu>
|
||||
|
||||
To share resources at 75% and 25%, we can use the `cpu.shares` file in the `cpu` cgroup. We assign a share value to the "high" group that is three times higher than that of the "low" group:
|
||||
|
||||
```bash
|
||||
|> echo 75 > /sys/fs/cgroup/cpu/high/cpu.shares
|
||||
|> echo 25 > /sys/fs/cgroup/cpu/low/cpu.shares
|
||||
```
|
||||
|
||||
After running the test program in each shell, we can observe in @shared-cpu that the processes in the "high" #gls("cgroup", long: false) are allocated 75% of the CPU capacity, while those in the "low" #gls("cgroup", long: false) receive 25%:
|
||||
```bash
|
||||
# In the first shell, add it to the "low" cgroup and run the test program
|
||||
|> . ./shared-cpu.sh low
|
||||
|
||||
# In the second shell, add it to the "high" cgroup and run the test program
|
||||
|> . ./shared-cpu.sh high
|
||||
```
|
||||
|
||||
|
||||
#figure(
|
||||
image("shared-cpu.png", width: 80%),
|
||||
caption: [CPU usage of the two programs with shared resources]
|
||||
)<shared-cpu>
|
||||
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 180 KiB |
@@ -1,283 +0,0 @@
|
||||
#import "/doc/metadata.typ": *
|
||||
|
||||
= Linux System Optimisation
|
||||
|
||||
In this laboratory, the usage of `#gls("perf", long: false)` as a performance analysis tool is explored.
|
||||
|
||||
|
||||
== Exercise 1
|
||||
|
||||
#task([
|
||||
Measure the performance of `ex1`
|
||||
],[
|
||||
```
|
||||
Performance counter stats for './ex1':
|
||||
|
||||
40609.10 msec task-clock # 1.000 CPUs utilized
|
||||
22 context-switches # 0.542 /sec
|
||||
0 cpu-migrations # 0.000 /sec
|
||||
48867 page-faults # 1.203 K/sec
|
||||
33136692484 cycles # 0.816 GHz
|
||||
1671194529 instructions # 0.05 insn per cycle
|
||||
269592231 branches # 6.639 M/sec
|
||||
1013366 branch-misses # 0.38% of all branches
|
||||
|
||||
40.618926728 seconds time elapsed
|
||||
|
||||
39.901620000 seconds user
|
||||
0.296158000 seconds sys
|
||||
|
||||
```
|
||||
This program performs 22 context switches and takes 40.6 seconds to run.
|
||||
])
|
||||
|
||||
#task([
|
||||
What error is present in the `ex1` program?
|
||||
],[
|
||||
The error lies in how the array memory is accessed. In C, 2D arrays are stored in "row-major" order, meaning elements of the same row are contiguous in memory. However, the original code accesses the array using `array[j][i]` within the loops, where the row index `j` is in the inner loop.
|
||||
|
||||
This causes the program to jump across memory addresses non-sequentially, triggering a cache miss almost every time. This can be solved by simply swapping the indices to `array[i][j]` (or swapping the loop order) to process memory sequentially:
|
||||
|
||||
|
||||
```c
|
||||
int i, j;
|
||||
for (i = 0; i < SIZE; i++)
|
||||
{
|
||||
for (j = 0; j < SIZE; j++)
|
||||
{
|
||||
array[i][j]+= 10;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
With these modifications, the performance is improved by a factor of nearly 80.
|
||||
|
||||
```
|
||||
Performance counter stats for './optimized':
|
||||
|
||||
474.62 msec task-clock # 0.940 CPUs utilized
|
||||
15 context-switches # 31.604 /sec
|
||||
0 cpu-migrations # 0.000 /sec
|
||||
48866 page-faults # 102.959 K/sec
|
||||
387200454 cycles # 0.816 GHz
|
||||
253128815 instructions # 0.65 insn per cycle
|
||||
39724528 branches # 83.698 M/sec
|
||||
577317 branch-misses # 1.45% of all branches
|
||||
|
||||
0.505146917 seconds time elapsed
|
||||
|
||||
0.233682000 seconds user
|
||||
0.237584000 seconds sys
|
||||
|
||||
```
|
||||
|
||||
This can be observed by running the same performance analysis with `#gls("perf", long: false)`. The elapsed time drops from around 40 seconds to approximately 0.5 seconds. A similar improvement can be observed in the cache misses:
|
||||
- optimized : 753,502
|
||||
- basic : 406,627,550
|
||||
|
||||
|
||||
])
|
||||
|
||||
|
||||
#task([
|
||||
Show `#gls("l1", long: false)` cache misses for `ex1`:
|
||||
],[
|
||||
#table(
|
||||
columns: (1.5fr, 1fr),
|
||||
stroke: none,
|
||||
[
|
||||
Not optimized
|
||||
```
|
||||
407036282 L1-dcache-load-misses
|
||||
|
||||
39.868545227 seconds time elapsed
|
||||
39.115950000 seconds user
|
||||
0.347522000 seconds sys
|
||||
|
||||
```
|
||||
],[
|
||||
Optimized
|
||||
```
|
||||
42027157 L1-dcache-load-misses
|
||||
|
||||
4.132272210 seconds time elapsed
|
||||
3.778635000 seconds user
|
||||
0.296472000 seconds sys
|
||||
```
|
||||
]
|
||||
)
|
||||
There is still an approximate 10-fold difference between the two configurations' `#gls("l1", long: false)` cache misses.
|
||||
])
|
||||
|
||||
|
||||
#task([Events analysed with `#gls("perf", long: false)`:],[
|
||||
|
||||
- *Instructions*: Indicates the total number of `#gls("cpu", long: false)` instructions executed while the program is running.
|
||||
- *Cache-misses*: This occurs when the required data is not currently stored in the cache hierarchy, forcing the processor to fetch it from slower main memory (`#gls("ram", long: false)`).
|
||||
- *Branch-misses*: Occurs during conditional branching when the `#gls("cpu", long: false)`'s branch predictor incorrectly guesses the next instruction path, resulting in pipeline flushes.
|
||||
- *L1-dcache-load-misses*: Occurs when the requested data is not present in the Level 1 Data Cache (`#gls("l1", long: false)` dcache), requiring a lookup in the next cache level (`#gls("l2", long: false)` cache).
|
||||
- *CPU-migrations*: Indicates the number of times the operating system scheduler moved the program threads from one `#gls("cpu", long: false)` core to another.
|
||||
- *Context-switches*: Occurs when the process relinquishes the `#gls("cpu", long: false)` core to allow other processes to run. This context-switch requires saving and restoring processor registers, including the `#gls("pc", long: false)`.
|
||||
|
||||
])
|
||||
|
||||
|
||||
#task([Timing performance of `#gls("perf", long: false)`], [
|
||||
Below are several execution times for the optimized program:
|
||||
|
||||
#figure(table(
|
||||
columns: (1fr, 1fr),
|
||||
// stroke: none,
|
||||
[*Without `#gls("perf", long: false)`*], [*With `#gls("perf", long: false)`*],
|
||||
[
|
||||
```
|
||||
real 0m 4.44s
|
||||
user 0m 3.83s
|
||||
sys 0m 0.29s
|
||||
|
||||
```
|
||||
],
|
||||
[
|
||||
```
|
||||
real 0m 4.38s
|
||||
user 0m 4.05s
|
||||
sys 0m 0.27s
|
||||
|
||||
```
|
||||
],[
|
||||
```
|
||||
real 0m 4.75s
|
||||
user 0m 4.09s
|
||||
sys 0m 0.34s
|
||||
|
||||
```
|
||||
],[
|
||||
```
|
||||
real 0m 4.75s
|
||||
user 0m 4.09s
|
||||
sys 0m 0.34s
|
||||
|
||||
```
|
||||
],
|
||||
),
|
||||
caption:[Impact of the `#gls("perf", long: false)` tool]
|
||||
)<impact-perf>
|
||||
|
||||
As seen in @impact-perf, running the program with `#gls("perf", long: false)` does not introduce a significant performance overhead, which can be attributed to stable `#gls("cpu", long: false)` core scheduling and allocation.
|
||||
|
||||
])
|
||||
|
||||
== Exercise 2
|
||||
|
||||
The program fills an array with random numbers between 0 and 512. Then, it iterates 10,000 times over the entire array to sum all elements that are greater than or equal to 256.
|
||||
|
||||
|
||||
#figure(
|
||||
table(
|
||||
columns: (1fr),
|
||||
[Without Optimisation],
|
||||
[
|
||||
```
|
||||
|
||||
26170.47 msec task-clock # 1.000 CPUs utilized
|
||||
17 context-switches # 0.650 /sec
|
||||
0 cpu-migrations # 0.000 /sec
|
||||
74 page-faults # 2.828 /sec
|
||||
21354981945 cycles # 0.816 GHz
|
||||
14768657990 instructions # 0.69 insn per cycle
|
||||
988541451 branches # 37.773 M/sec
|
||||
327869867 branch-misses # 33.17% of all branches
|
||||
|
||||
26.178296596 seconds time elapsed
|
||||
|
||||
26.117025000 seconds user
|
||||
0.003961000 seconds sys
|
||||
```
|
||||
], [With "sort" optimisation],[
|
||||
```
|
||||
23430.74 msec task-clock
|
||||
17 context-switches # 0.726 /sec
|
||||
0 cpu-migrations # 0.000 /sec
|
||||
109 page-faults # 4.652 /sec
|
||||
19119368029 cycles # 0.816 GHz
|
||||
14818405467 instructions # 0.78 insn per cycle
|
||||
997843744 branches # 42.587 M/sec
|
||||
805002 branch-misses # 0.08% of all branches
|
||||
|
||||
23.439504220 seconds time elapsed
|
||||
|
||||
23.382177000 seconds user
|
||||
0.003961000 seconds sys
|
||||
```
|
||||
]
|
||||
),
|
||||
caption:[Ex02 timing optimisation]
|
||||
)<sort-optimization>
|
||||
|
||||
In @sort-optimization, there is a gain of around 3 seconds due to a massive decrease in branch misses, dropping from 33.17% to 0.08%.
|
||||
|
||||
This is explained by the `#gls("cpu", long: false)`'s branch predictor. Inside the loop, the program checks if the value is `>= 256`. When the array is filled with random numbers, the processor cannot predict the outcome of this condition, resulting in frequent pipeline flushes. However, when the array is sorted, the condition is always false for the first half of the array, and always true for the second half. The `#gls("cpu", long: false)` easily predicts this pattern, avoiding branch misses and executing much faster.
|
||||
|
||||
The same test was performed with the `-O1` optimisation flag, and there is almost no difference between the two scripts. The optimized version is around 4.12s and the basic version is around 4.6s. The difference of 0.6 seconds can be explained by the sorting algorithm itself in the optimized version, as sorting is the only added operation.
|
||||
|
||||
|
||||
== Exercise 3
|
||||
By analysing the call graph with `#gls("perf", long: false) report`, we can trace the indirect calls to `std::operator==<char>` back to our application. The bottleneck originates in the `HostCounter::isNewHost` function, specifically during the `std::find` operation on a `std::vector`:
|
||||
|
||||
```c
|
||||
bool HostCounter::isNewHost(std::string hostname)
|
||||
{
|
||||
return std::find(myHosts.begin(), myHosts.end(), hostname) == myHosts.end();
|
||||
}
|
||||
```
|
||||
|
||||
Searching through an unsorted vector requires a linear comparison of strings ($O(N)$ complexity), which is highly inefficient. As shown below, processing just a sample of the logs takes over 2 minutes:
|
||||
|
||||
```
|
||||
|> time ./read-apache-logs access_log_NASA_Jul95_samples
|
||||
Processing log file access_log_NASA_Jul95_samples
|
||||
Found 14867 unique Hosts/IPs
|
||||
real 2m 15.58s
|
||||
user 2m 14.68s
|
||||
sys 0m 0.12s
|
||||
|
||||
```
|
||||
To fix this, the data structure must be changed from `std::vector` to `std::set`. A set uses a tree-based structure, reducing the search complexity to $O(log N)$ (or $O(1)$.
|
||||
|
||||
|
||||
#figure(
|
||||
image("command-after-optimization.png"),
|
||||
caption:[ `#gls("perf", long: false)` report after migrating to `std::set`]
|
||||
)<command-opti>
|
||||
|
||||
After applying these changes, the `#gls("perf", long: false)` report in @command-opti shows a much healthier execution profile. The execution time drops drastically, creating a massive performance gap compared to the initial `std::vector` implementation:
|
||||
```
|
||||
|> time ./read-apache-logs access_log_NASA_Jul95_samples
|
||||
Processing log file access_log_NASA_Jul95_samples
|
||||
Found 14867 unique Hosts/IPs
|
||||
real 0m 1.55s
|
||||
user 0m 1.36s
|
||||
sys 0m 0.10s
|
||||
```
|
||||
|
||||
Even when processing the entire log file containing roughly 2 million entries, the optimized program finishes in under 15 seconds:
|
||||
```
|
||||
|> time ./read-apache-logs access_log_NASA_Jul95
|
||||
Processing log file access_log_NASA_Jul95
|
||||
Found 81983 unique Hosts/#gls("ip", long: false)s
|
||||
real 0m 14.76s
|
||||
user 0m 13.90s
|
||||
sys 0m 0.68s
|
||||
```
|
||||
|
||||
|
||||
#task([Measure interruption latency and jitter], [
|
||||
To measure latency and jitter, a hardware-based approach using an oscilloscope and a square-wave generator was implemented.
|
||||
First, the generator toggles a processor pin to trigger the interrupt routine. Then, another pin creates a pulse as a response, which is measured by the oscilloscope. The latency is the delay between the generator's rising edge and the response pulse. The jitter is the variation of this latency over multiple measurements.
|
||||
|
||||
To differentiate between Kernel Space and User Space:
|
||||
- *Kernel Space*: The response pin is toggled directly inside the kernel's Interrupt Service Routine (`#gls("irq", long: false)` handler / driver).
|
||||
- *User Space*: The response pin is toggled by a user application that wakes up (using `#gls("epoll", long: false)()`) after the kernel has handled the interrupt.
|
||||
|
||||
The difference between these two latency measurements represents the context-switch overhead from kernel mode to user mode.
|
||||
])
|
||||
@@ -34,15 +34,9 @@
|
||||
date: date,
|
||||
tableof: tableof,
|
||||
)
|
||||
#import "@preview/codly:1.3.0": *
|
||||
#import "@preview/codly-languages:0.1.8": *
|
||||
#show: codly-init.with()
|
||||
|
||||
#codly(languages: codly-languages)
|
||||
|
||||
#v(5em)
|
||||
#infobox()[
|
||||
The repository for these lab can be found at the following address:
|
||||
The repository for this labs can be found at the following address:
|
||||
|
||||
#align(center)[https://github.com/Klagarge/MSE-MA-CSEL]
|
||||
]
|
||||
@@ -50,136 +44,13 @@
|
||||
|
||||
//-------------------------------------
|
||||
// Content
|
||||
//
|
||||
#let general-architecture = [
|
||||
#figure(
|
||||
image("mini-project/deployement.png", width: 100%),
|
||||
caption: "General architecture"
|
||||
) <fig:general-architecture>
|
||||
]
|
||||
//
|
||||
|
||||
= Mini-Project
|
||||
|
||||
= Introduction
|
||||
|
||||
The purpose of this mini-project is to train different concept we saw during the semester.
|
||||
We simulate a fan controlled by the temperature of the @cpu. To simulate this fan, we blink the status @led.
|
||||
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 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
|
||||
|
||||
#pagebreak()
|
||||
= Architecture
|
||||
|
||||
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 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 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 action in the module:
|
||||
- read temperature
|
||||
- set and get mode
|
||||
- set period
|
||||
- get period
|
||||
|
||||
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 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.
|
||||
|
||||
#figure(
|
||||
[```c
|
||||
typedef enum {
|
||||
LED_STATUS,
|
||||
LED_POWER,
|
||||
} LED_type; // enum to choose which led we want initialize
|
||||
|
||||
typedef struct {
|
||||
int gpio;
|
||||
} LED; // Type for our led class
|
||||
|
||||
LED* LED_init(LED_type type); // Create new LED object
|
||||
void LED_on(LED* led);
|
||||
void LED_off(LED* led);
|
||||
void LED_toggle(LED* led);
|
||||
```],
|
||||
caption: "Led class header"
|
||||
) <fig:led-class-header>
|
||||
|
||||
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
|
||||
typedef void (*BTN_callback)();
|
||||
|
||||
BTN* BTN_init(BTN_type type);
|
||||
void BTN_set_callback(BTN* btn, BTN_callback callback);
|
||||
```],
|
||||
caption: "Button class header"
|
||||
) <fig:button-class-header>
|
||||
|
||||
=== #gls("ipc", long: false)
|
||||
|
||||
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 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/
|
||||
rsync -a /workspace/config/configs/ /buildroot/configs/
|
||||
cd /buildroot
|
||||
make linux-rebuild
|
||||
make uboot-rebuild
|
||||
make
|
||||
```
|
||||
|
||||
Then, if we boot with @tftp, we can simply reboot. Otherwise, we have to reflash the sd card with the new image.
|
||||
|
||||
=== 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.
|
||||
|
||||
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 makes the animation until the counter reach 0. Then it waits with the `pthread_cond_wait`.
|
||||
|
||||
|
||||
|
||||
== #gls("cli", long: false)
|
||||
|
||||
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 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)
|
||||
|
||||
|
||||
= Conclusion
|
||||
Really fun, but sadly not enough time for more over-engineering. #emoji.face.cry
|
||||
#lorem(150)
|
||||
|
||||
#lorem(50)
|
||||
|
||||
|
||||
//-------------------------------------
|
||||
@@ -187,3 +58,4 @@ Really fun, but sadly not enough time for more over-engineering. #emoji.face.cry
|
||||
//
|
||||
#heading(numbering:none, outlined: false)[] <sec:end>
|
||||
#make_glossary(gloss:gloss, title:i18n("gloss-title"))
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 138 KiB |
@@ -1,3 +0,0 @@
|
||||
<diagram program="umletino" version="15.1"><zoom_level>9</zoom_level><element><id>UMLDeployment</id><coordinates><x>63</x><y>324</y><w>342</w><h>234</h></coordinates><panel_attributes>Kernel module</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLDeployment</id><coordinates><x>441</x><y>324</y><w>342</w><h>234</h></coordinates><panel_attributes>Daemon</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLDeployment</id><coordinates><x>675</x><y>261</y><w>81</w><h>45</h></coordinates><panel_attributes>CLI</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLGeneric</id><coordinates><x>81</x><y>495</y><w>126</w><h>36</h></coordinates><panel_attributes>blink</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLGeneric</id><coordinates><x>243</x><y>495</y><w>126</w><h>36</h></coordinates><panel_attributes>temperature</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLGeneric</id><coordinates><x>243</x><y>369</y><w>126</w><h>36</h></coordinates><panel_attributes>sysfs</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLGeneric</id><coordinates><x>162</x><y>432</y><w>126</w><h>36</h></coordinates><panel_attributes>symbol=component
|
||||
regulator</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>216</x><y>378</y><w>45</w><h>72</h></coordinates><panel_attributes>lt=<-</panel_attributes><additional_attributes>30;10;10;10;10;60</additional_attributes></element><element><id>Relation</id><coordinates><x>117</x><y>441</y><w>63</w><h>72</h></coordinates><panel_attributes>lt=-></panel_attributes><additional_attributes>50;10;10;10;10;60</additional_attributes></element><element><id>Relation</id><coordinates><x>279</x><y>441</y><w>63</w><h>72</h></coordinates><panel_attributes>lt=-></panel_attributes><additional_attributes>10;10;50;10;50;60</additional_attributes></element><element><id>UMLGeneric</id><coordinates><x>459</x><y>495</y><w>126</w><h>36</h></coordinates><panel_attributes>gpio</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLGeneric</id><coordinates><x>621</x><y>369</y><w>126</w><h>36</h></coordinates><panel_attributes>ipc</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLGeneric</id><coordinates><x>621</x><y>495</y><w>126</w><h>36</h></coordinates><panel_attributes>oled</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLGeneric</id><coordinates><x>549</x><y>432</y><w>126</w><h>36</h></coordinates><panel_attributes>symbol=component
|
||||
application</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>360</x><y>378</y><w>225</w><h>72</h></coordinates><panel_attributes>lt=<-</panel_attributes><additional_attributes>10;10;230;10;230;60</additional_attributes></element><element><id>Relation</id><coordinates><x>504</x><y>441</y><w>63</w><h>72</h></coordinates><panel_attributes>lt=-></panel_attributes><additional_attributes>50;10;10;10;10;60</additional_attributes></element><element><id>Relation</id><coordinates><x>666</x><y>441</y><w>63</w><h>72</h></coordinates><panel_attributes>lt=-></panel_attributes><additional_attributes>10;10;50;10;50;60</additional_attributes></element><element><id>Relation</id><coordinates><x>594</x><y>378</y><w>45</w><h>72</h></coordinates><panel_attributes>lt=<-</panel_attributes><additional_attributes>30;10;10;10;10;60</additional_attributes></element><element><id>Relation</id><coordinates><x>702</x><y>297</y><w>27</w><h>90</h></coordinates><panel_attributes>lt=<-</panel_attributes><additional_attributes>10;80;10;10</additional_attributes></element></diagram>
|
||||
@@ -27,208 +27,6 @@
|
||||
description: "Rust is a modern systems programming language focused on safety, speed, and concurrency. It prevents common programming errors such as null pointer dereferencing and data races at compile time, making it a preferred choice for performance-critical applications.",
|
||||
group: "Programming Language"
|
||||
),
|
||||
(
|
||||
key: "csel",
|
||||
short: "CSEL",
|
||||
long: "Conception de Systèmes Embarqués sous Linux",
|
||||
description: "Embedded Linux Systems Design course at HES-SO, covering kernel development, driver programming, and system optimization.",
|
||||
group: "Course"
|
||||
),
|
||||
(
|
||||
key: "cpu",
|
||||
short: "CPU",
|
||||
long: "Central Processing Unit",
|
||||
description: "The primary component of a computer that performs most of the processing inside the computer, executing instructions of computer programs.",
|
||||
group: "Hardware"
|
||||
),
|
||||
(
|
||||
key: "l1",
|
||||
short: "L1",
|
||||
long: "Level 1 Cache",
|
||||
description: "The primary cache of a CPU, typically built directly into the processor chip, representing the fastest but smallest cache level closest to the execution units.",
|
||||
group: "Hardware"
|
||||
),
|
||||
(
|
||||
key: "l2",
|
||||
short: "L2",
|
||||
long: "Level 2 Cache",
|
||||
description: "A secondary cache that is larger but slightly slower than the L1 cache, serving to catch cache misses from the L1 cache before querying system memory.",
|
||||
group: "Hardware"
|
||||
),
|
||||
(
|
||||
key: "ram",
|
||||
short: "RAM",
|
||||
long: "Random-Access Memory",
|
||||
description: "A form of volatile computer memory that can be read and changed in any order, used to store working data and machine code currently in use.",
|
||||
group: "Hardware"
|
||||
),
|
||||
(
|
||||
key: "pc",
|
||||
short: "PC",
|
||||
long: "Program Counter",
|
||||
description: "A processor register that indicates where the computer is in its program sequence, holding the address of the next instruction to be executed.",
|
||||
group: "Hardware"
|
||||
),
|
||||
(
|
||||
key: "led",
|
||||
short: "LED",
|
||||
plural: "LEDS",
|
||||
long: "Light Emitting Diode",
|
||||
description: "A semiconductor light source that emits light when current flows through it.",
|
||||
group: "Hardware"
|
||||
),
|
||||
(
|
||||
key: "gpio",
|
||||
short: "GPIO",
|
||||
plural: "GPIOs",
|
||||
long: "General-Purpose Input/Output",
|
||||
description: "Uncommitted digital signal pins on an integrated circuit or electronic circuit board whose behavior can be programmed as input or output at runtime.",
|
||||
group: "Hardware"
|
||||
),
|
||||
(
|
||||
key: "pid",
|
||||
short: "PID",
|
||||
plural: "PIDs",
|
||||
long: "Process Identifier",
|
||||
description: "A unique numerical identifier assigned by the operating system kernel to each active process, used for managing, scheduling, and tracking processes.",
|
||||
group: "Operating System"
|
||||
),
|
||||
(
|
||||
key: "irq",
|
||||
short: "IRQ",
|
||||
plural: "IRQs",
|
||||
long: "Interrupt Request",
|
||||
description: "A signal sent to the processor that temporarily suspends the current program execution to allow an Interrupt Service Routine (ISR) to run in response to a hardware event.",
|
||||
group: "Operating System"
|
||||
),
|
||||
(
|
||||
key: "gpu",
|
||||
short: "GPU",
|
||||
long: "Graphics Processing Unit",
|
||||
description: "A specialized electronic circuit designed to accelerate graphics rendering and parallel computing tasks.",
|
||||
group: "Hardware"
|
||||
),
|
||||
(
|
||||
key: "cuda",
|
||||
short: "CUDA",
|
||||
long: "Compute Unified Device Architecture",
|
||||
description: "A parallel computing platform and application programming interface model created by NVIDIA.",
|
||||
group: "Programming API"
|
||||
),
|
||||
(
|
||||
key: "openmp",
|
||||
short: "OpenMP",
|
||||
long: "Open Multi-Processing",
|
||||
description: "An application programming interface that supports multi-platform shared-memory multiprocessing programming.",
|
||||
group: "Programming API"
|
||||
),
|
||||
(
|
||||
key: "io",
|
||||
short: "I/O",
|
||||
long: "Input/Output",
|
||||
description: "The communication between an information processing system (such as a computer) and the outside world.",
|
||||
group: "Computer Science"
|
||||
),
|
||||
(
|
||||
key: "ip",
|
||||
short: "IP",
|
||||
plural: "IPs",
|
||||
long: "Internet Protocol",
|
||||
description: "The principal communications protocol in the Internet protocol suite for relaying datagrams across network boundaries.",
|
||||
group: "Computer Science"
|
||||
),
|
||||
(
|
||||
key: "oom",
|
||||
short: "OOM",
|
||||
long: "Out of Memory",
|
||||
description: "A state of computer operation where no additional memory can be allocated, often leading to the invocation of an OOM killer to terminate processes.",
|
||||
group: "Operating System"
|
||||
),
|
||||
(
|
||||
key: "sysfs",
|
||||
short: "sysfs",
|
||||
long: "System Filesystem",
|
||||
description: "A virtual pseudo-filesystem provided by the Linux kernel that exports information about hardware, device drivers, and kernel subsystems to user space.",
|
||||
group: "Operating System"
|
||||
),
|
||||
(
|
||||
key: "syslog",
|
||||
short: "syslog",
|
||||
long: "System Logging",
|
||||
description: "A standard protocol and utility for system message logging in UNIX and Linux systems, allowing applications to log messages to files, consoles, or remote syslog daemons.",
|
||||
group: "Operating System"
|
||||
),
|
||||
(
|
||||
key: "perf",
|
||||
short: "perf",
|
||||
long: "Performance Events for Linux",
|
||||
description: "A powerful performance supervising and analyzing tool in Linux, capable of profiling hardware performance counters, tracepoints, software performance counters, and dynamic probes.",
|
||||
group: "Operating System"
|
||||
),
|
||||
(
|
||||
key: "epoll",
|
||||
short: "epoll",
|
||||
long: "Event Poll",
|
||||
description: "A scalable Linux I/O event notification facility designed to monitor multiple file descriptors with high efficiency.",
|
||||
group: "Operating System"
|
||||
),
|
||||
(
|
||||
key: "cgroup",
|
||||
short: "cgroup",
|
||||
plural: "cgroups",
|
||||
long: "Control Groups",
|
||||
description: "A Linux kernel feature that limits, polices, and isolates resource usage (such as CPU, memory, and disk I/O) for groups of processes.",
|
||||
group: "Operating System"
|
||||
),
|
||||
(
|
||||
key: "mib",
|
||||
short: "MiB",
|
||||
plural: "MiBs",
|
||||
long: "Mebibyte",
|
||||
description: "A unit of digital information equal to 1,048,576 bytes (2^20 bytes).",
|
||||
group: "Computer Science"
|
||||
),
|
||||
(
|
||||
key: "ipc",
|
||||
short: "IPC",
|
||||
long: "Inter-Process Communication",
|
||||
description: "A set of programming interfaces that allow processes to communicate with each other and synchronize their actions.",
|
||||
group: "Operating System"
|
||||
),
|
||||
(
|
||||
key: "tftp",
|
||||
short: "TFTP",
|
||||
long: "Trivial File Transfer Protocol",
|
||||
description: "A simple protocol for transferring files, typically used for booting devices over a network.",
|
||||
group: "Networking"
|
||||
),
|
||||
(
|
||||
key: "cli",
|
||||
short: "CLI",
|
||||
long: "Command Line Interface",
|
||||
description: "A text-based interface used to interact with software and operating systems by typing commands into a terminal.",
|
||||
group: "Computer Science"
|
||||
),
|
||||
(
|
||||
key: "api",
|
||||
short: "API",
|
||||
long: "Application Programming Interface",
|
||||
description: "A set of defined rules that enable different software applications to communicate with each other.",
|
||||
group: "Computer Science"
|
||||
),
|
||||
(
|
||||
key: "i2c",
|
||||
short: "I2C",
|
||||
long: "Inter-Integrated Circuit",
|
||||
description: "A synchronous, multi-controller/multi-target, single-ended, serial communication bus used for attaching lower-speed peripheral ICs to processors and microcontrollers.",
|
||||
group: "Hardware"
|
||||
),
|
||||
(
|
||||
key: "oled",
|
||||
short: "OLED",
|
||||
description: "A light-emitting diode (LED) in which the emissive electroluminescent layer is a film of organic compound that emits light in response to an electric current.",
|
||||
group: "Hardware"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -256,7 +54,6 @@
|
||||
show-all: false,
|
||||
// disable the back ref at the end of the descriptions
|
||||
disable-back-references: false,
|
||||
shorthands: ("plural", "capitalize", "capitalize-plural", "short", "long"),
|
||||
)
|
||||
]}
|
||||
]}
|
||||
|
||||
@@ -16,7 +16,7 @@ void HostCounter::notifyHost(std::string hostname)
|
||||
// add the host in the list if not already in
|
||||
if(isNewHost(hostname))
|
||||
{
|
||||
myHosts.insert(hostname);
|
||||
myHosts.push_back(hostname);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <string>
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
class HostCounter
|
||||
{
|
||||
@@ -18,5 +18,5 @@ class HostCounter
|
||||
// check if host is already in the list
|
||||
bool isNewHost(std::string hostname);
|
||||
|
||||
std::set< std::string > myHosts;
|
||||
std::vector< std::string > myHosts;
|
||||
};
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
#include "button.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/epoll.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) {
|
||||
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;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#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
|
||||
@@ -1,48 +0,0 @@
|
||||
#include "led.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <pthread.h>
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#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
|
||||
@@ -1,159 +1,101 @@
|
||||
/**
|
||||
* Copyright 2018 University of Applied Sciences Western Switzerland / Fribourg
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Project: HEIA-FR / HES-SO MSE - MA-CSEL1 Laboratory
|
||||
*
|
||||
* Abstract: System programming - file system
|
||||
*
|
||||
* Purpose: NanoPi silly status led control system
|
||||
*
|
||||
* Autĥor: Daniel Gachet
|
||||
* Date: 07.11.2018
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <pthread.h>
|
||||
#include <syslog.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include "timer.h"
|
||||
#include "led.h"
|
||||
#include "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 GPIO_EXPORT "/sys/class/gpio/export"
|
||||
#define GPIO_UNEXPORT "/sys/class/gpio/unexport"
|
||||
#define GPIO_LED "/sys/class/gpio/gpio10"
|
||||
#define LED "10"
|
||||
#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};
|
||||
static int open_led() {
|
||||
// unexport pin out of sysfs (reinitialization)
|
||||
int f = open(GPIO_UNEXPORT, O_WRONLY);
|
||||
write(f, LED, strlen(LED));
|
||||
close(f);
|
||||
|
||||
// export pin to sysfs
|
||||
f = open(GPIO_EXPORT, O_WRONLY);
|
||||
write(f, LED, strlen(LED));
|
||||
close(f);
|
||||
|
||||
void* btn_thread(void* arg) {
|
||||
ThreadData* data = (ThreadData*)arg;
|
||||
// config pin
|
||||
f = open(GPIO_LED "/direction", O_WRONLY);
|
||||
write(f, "out", 3);
|
||||
close(f);
|
||||
|
||||
// Open all button with the right flags
|
||||
int btn[NBR_BTN] = {0};
|
||||
for(int i=0; i<NBR_BTN; i++) {
|
||||
btn[i] = btn_open(GPIO_BTN[i], BTN[i]);
|
||||
if (btn[i] < 0) {
|
||||
perror("Failed to open button");
|
||||
}
|
||||
}
|
||||
// open gpio value attribute
|
||||
f = open(GPIO_LED "/value", O_RDWR);
|
||||
return f;
|
||||
}
|
||||
|
||||
// Create epoll instance to control all button files
|
||||
int epfd = epoll_create1(0);
|
||||
if (epfd < 0) {
|
||||
perror("Failed to create epoll");
|
||||
}
|
||||
void led_on(int led) {
|
||||
pwrite(led, "1", sizeof("1"), 0);
|
||||
}
|
||||
|
||||
// Add buttons to epoll
|
||||
struct epoll_event ev[NBR_BTN];
|
||||
// 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)
|
||||
for(int i=0; i<NBR_BTN; i++) {
|
||||
ev[i].events = EPOLLIN | EPOLLERR | EPOLLET;
|
||||
ev[i].data.fd = btn[i];
|
||||
if (epoll_ctl(epfd, EPOLL_CTL_ADD, btn[i], &ev[i]) < 0) {
|
||||
perror("Failed to add button to epoll");
|
||||
}
|
||||
}
|
||||
|
||||
// Dummy read to clear initial state before waiting
|
||||
char buf[2];
|
||||
for(int i=0; i<NBR_BTN; i++) {
|
||||
pread(btn[i], buf, sizeof(buf), 0);
|
||||
}
|
||||
|
||||
printf("Waiting for button presses...\n");
|
||||
|
||||
// Event main loop
|
||||
while (1) {
|
||||
struct epoll_event events[NBR_BTN];
|
||||
|
||||
// Timeout is -1: Block infinitely until an event occurs!
|
||||
int n = epoll_wait(epfd, events, 1, -1);
|
||||
|
||||
if (n < 0) {
|
||||
perror("epoll_wait error");
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
// read btn file
|
||||
pread(events[i].data.fd, buf, sizeof(buf), 0);
|
||||
|
||||
if (events[i].data.fd == btn[0]) {
|
||||
if (buf[0] == '1') {
|
||||
data->flash_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; i<NBR_BTN; i++) {
|
||||
close(btn[i]);
|
||||
}
|
||||
close(epfd);
|
||||
void led_off(int led) {
|
||||
pwrite(led, "0", sizeof("0"), 0);
|
||||
}
|
||||
|
||||
static void* timer_thread(void* arg) {
|
||||
ThreadData* data = (ThreadData*)arg;
|
||||
|
||||
int led = led_open(GPIO_LED, LED);
|
||||
|
||||
int led = open_led();
|
||||
led_off(led);
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
||||
struct epoll_event ev;
|
||||
|
||||
|
||||
int isLedOn = 0;
|
||||
int k = 0;
|
||||
while(1) {
|
||||
|
||||
int n = epoll_wait(data->epoll_fd, &ev, 1, -1);
|
||||
@@ -168,13 +110,12 @@ static void* timer_thread(void* arg) {
|
||||
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);
|
||||
printf("ping %d\n", k++);
|
||||
isLedOn = 1;
|
||||
} else {
|
||||
delay = time_off_ms; // rest of the period
|
||||
@@ -188,11 +129,11 @@ static void* timer_thread(void* arg) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char* 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;
|
||||
|
||||
@@ -215,15 +156,7 @@ int main(int argc, char* argv[]) {
|
||||
exit(30);
|
||||
}
|
||||
|
||||
pthread_join(thread, NULL);
|
||||
|
||||
// Setup button thread
|
||||
pthread_t btn_thread_inst;
|
||||
pthread_create(&btn_thread_inst, NULL, btn_thread, &data);
|
||||
|
||||
while (1) {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
closelog();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
#include "timer.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/timerfd.h>
|
||||
@@ -25,11 +23,11 @@ int timer_create_empty() {
|
||||
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;
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
#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
|
||||
void timer_link_to_epoll(int* timer_fd, int* epoll_fd);
|
||||
@@ -1,7 +0,0 @@
|
||||
CompileFlags:
|
||||
Add:
|
||||
# Architecture and cross-compilation
|
||||
- "--target=aarch64-linux-gnu"
|
||||
|
||||
# Setup sysroot for buildroot
|
||||
- "--sysroot=/buildroot/output/host/aarch64-buildroot-linux-gnu/sysroot"
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
|
||||
process:
|
||||
$(MAKE) EXE=process SRCS=process.c all
|
||||
|
||||
cgroups:
|
||||
$(MAKE) EXE=cgroups SRCS=cgroups.c all
|
||||
|
||||
max-cpu:
|
||||
$(MAKE) EXE=max-cpu SRCS=max-cpu.c all
|
||||
|
||||
# Include the standard application Makefile for the CSEL1 labs
|
||||
include ../appl.mk
|
||||
@@ -1,44 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define NUM_BLOCKS 50
|
||||
#define BLOCK_SIZE (1024 * 1024) // 1 MiB
|
||||
// 2^20 = 1048576
|
||||
// 1024*1024 = 1048576
|
||||
|
||||
int main() {
|
||||
void *blocks[NUM_BLOCKS];
|
||||
int i;
|
||||
|
||||
printf("Allocate %d blocks of 1 MiB\n", NUM_BLOCKS);
|
||||
|
||||
for (i = 0; i < NUM_BLOCKS; i++) {
|
||||
blocks[i] = malloc(BLOCK_SIZE);
|
||||
|
||||
if (blocks[i] == NULL) {
|
||||
fprintf(stderr, "Error: Allocation not possible for block %d (Limit reached!)\n", i);
|
||||
|
||||
for (int j = 0; j < i; j++) {
|
||||
free(blocks[j]);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(blocks[i], 0, BLOCK_SIZE); // Touch the memory to ensure it's actually allocated
|
||||
|
||||
printf("Block %d allocated and initialized successfully.\n", i);
|
||||
|
||||
usleep(100000); // 100ms
|
||||
}
|
||||
|
||||
printf("Success: All blocks have been allocated.\n");
|
||||
|
||||
// Release the memory at the end
|
||||
for (i = 0; i < NUM_BLOCKS; i++) {
|
||||
free(blocks[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
mount -t tmpfs none /sys/fs/cgroup # Mount a temporary filesystem to /sys/fs/cgroup
|
||||
|
||||
# Memory
|
||||
mkdir /sys/fs/cgroup/memory # Create a directory for the memory cgroup
|
||||
mount -t cgroup -o memory cgroup /sys/fs/cgroup/memory # Mount the cgroup filesystem with memory
|
||||
mkdir /sys/fs/cgroup/memory/0 # Create a subdirectory for the memory cgroup
|
||||
echo $$ > /sys/fs/cgroup/memory/0/tasks # Add the current process to the memory cgroup
|
||||
echo 20M > /sys/fs/cgroup/memory/0/memory.limit_in_bytes # Set the memory limit to 20 MiB
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
@default:
|
||||
just --list
|
||||
|
||||
@build:
|
||||
make process
|
||||
make cgroups
|
||||
make max-cpu
|
||||
|
||||
@process:
|
||||
make process
|
||||
|
||||
@cgroups:
|
||||
make cgroups
|
||||
|
||||
@max-cpu:
|
||||
make max-cpu
|
||||
|
||||
@clean:
|
||||
rm -rf build
|
||||
rm -rf .obj
|
||||
rm -f -- process
|
||||
rm -f -- cgroups
|
||||
rm -f -- max-cpu
|
||||
@@ -1,70 +0,0 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <sched.h>
|
||||
|
||||
|
||||
int fork(void);
|
||||
|
||||
|
||||
volatile unsigned long counter = 0;
|
||||
void cpu_intensive_work(const char *process_name) {
|
||||
printf("%s (PID: %d) starting CPU-intensive work on CPU %d\n", process_name, getpid(), sched_getcpu());
|
||||
|
||||
while (1) {
|
||||
counter++;
|
||||
counter = (counter * counter + counter) % (counter * 1023);
|
||||
}
|
||||
}
|
||||
|
||||
void print_usage(const char *prog) {
|
||||
fprintf(stderr, "Usage: %s [--single] [--dual]\n", prog);
|
||||
fprintf(stderr, " --single Run single process (default)\n");
|
||||
fprintf(stderr, " --dual Run two processes\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int use_dual = 0;
|
||||
|
||||
if (argc > 1) {
|
||||
if (strcmp(argv[1], "--dual") == 0) {
|
||||
use_dual = 1;
|
||||
} else if (strcmp(argv[1], "--single") == 0) {
|
||||
use_dual = 0;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown option: %s\n", argv[1]);
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_dual) {
|
||||
// Dual-process mode
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0) {
|
||||
cpu_intensive_work("Child process");
|
||||
exit(0);
|
||||
} else if (pid > 0) {
|
||||
cpu_intensive_work("Parent process");
|
||||
wait(NULL);
|
||||
return 0;
|
||||
} else {
|
||||
perror("fork failed");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
// Single-process mode
|
||||
cpu_intensive_work("Process");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 {init|high|low}"
|
||||
echo " init - Initialize cgroup filesystem and cpuset groups"
|
||||
echo " high - Run ./max-cpu --dual in the 'high' cgroup (CPUs 2,3)"
|
||||
echo " low - Run ./max-cpu --dual in the 'low' cgroup (CPU 1)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
init)
|
||||
echo "Initializing cgroup filesystem..."
|
||||
|
||||
# Mount tmpfs for cgroup
|
||||
mount -t tmpfs none /sys/fs/cgroup 2>/dev/null
|
||||
|
||||
# Create and mount cpuset cgroup
|
||||
mkdir -p /sys/fs/cgroup/cpuset
|
||||
mount -t cgroup -o cpu,cpuset cpuset /sys/fs/cgroup/cpuset 2>/dev/null
|
||||
|
||||
# Create "low" cgroup and allocate CPU 1
|
||||
mkdir -p /sys/fs/cgroup/cpuset/low
|
||||
echo 1 > /sys/fs/cgroup/cpuset/low/cpuset.cpus
|
||||
echo 0 > /sys/fs/cgroup/cpuset/low/cpuset.mems
|
||||
|
||||
# Create "high" cgroup and allocate CPUs 2,3
|
||||
mkdir -p /sys/fs/cgroup/cpuset/high
|
||||
echo 2,3 > /sys/fs/cgroup/cpuset/high/cpuset.cpus
|
||||
echo 0 > /sys/fs/cgroup/cpuset/high/cpuset.mems
|
||||
|
||||
echo "Cgroup initialization complete."
|
||||
echo " - low group: CPU 1"
|
||||
echo " - high group: CPUs 2,3"
|
||||
;;
|
||||
|
||||
high)
|
||||
echo "Running ./max-cpu --dual in 'high' cgroup (CPUs 2,3)..."
|
||||
echo $$ > /sys/fs/cgroup/cpuset/high/tasks
|
||||
./max-cpu --dual
|
||||
;;
|
||||
|
||||
low)
|
||||
echo "Running ./max-cpu --dual in 'low' cgroup (CPU 1)..."
|
||||
echo $$ > /sys/fs/cgroup/cpuset/low/tasks
|
||||
./max-cpu --dual
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown command: $1"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
@@ -1,135 +0,0 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <string.h>
|
||||
#include <sched.h>
|
||||
#include <sys/resource.h>
|
||||
#include <signal.h>
|
||||
|
||||
const int NBR_MSG = 5;
|
||||
const char * MSG[] = {
|
||||
"Hallo, hallo !\0",
|
||||
"ça geht !\0",
|
||||
"Comment vont les olives ?\0",
|
||||
"Sacré trucs tes trucs là.\0",
|
||||
"Ta où les vaches !!!!!\0"
|
||||
};
|
||||
|
||||
static void catch_signal(int signal) {
|
||||
|
||||
switch (signal) {
|
||||
case SIGHUP:
|
||||
printf("SIGHUP received\n");
|
||||
break;
|
||||
case SIGINT:
|
||||
printf("SIGINT received\n");
|
||||
exit(EXIT_SUCCESS); // to avoid to be blocked and kill it with ctrl+c
|
||||
break;
|
||||
case SIGQUIT:
|
||||
printf("SIGQUIT received\n");
|
||||
break;
|
||||
case SIGTERM:
|
||||
printf("SIGTERM received\n");
|
||||
break;
|
||||
case SIGABRT:
|
||||
printf("SIGABRT received\n");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void install_catch_signal()
|
||||
{
|
||||
struct sigaction act = {
|
||||
.sa_handler = catch_signal,
|
||||
};
|
||||
sigemptyset(&act.sa_mask);
|
||||
sigaction(SIGHUP, &act, 0);
|
||||
sigaction(SIGINT, &act, 0);
|
||||
sigaction(SIGQUIT, &act, 0);
|
||||
sigaction(SIGTERM, &act, 0);
|
||||
sigaction(SIGABRT, &act, 0);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
install_catch_signal();
|
||||
|
||||
/* Setup socket for inter-process communication */
|
||||
int fd[2];
|
||||
int err = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
|
||||
if (err == -1) {
|
||||
perror("socketpair fail");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Prepare cpu set for process affinity */
|
||||
cpu_set_t set;
|
||||
CPU_ZERO(&set);
|
||||
int child_cpu = 0;
|
||||
int parent_cpu = 1;
|
||||
|
||||
/* Fork a child process */
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0) { /* Parent process */
|
||||
pid_t parent_pid = getpid();
|
||||
printf("Parent process: pid=%d\n", parent_pid);
|
||||
|
||||
/* Setup CPU for process */
|
||||
CPU_SET(child_cpu, &set);
|
||||
int ret = sched_setaffinity(parent_pid, sizeof(set), &set);
|
||||
if (ret == -1) {
|
||||
perror("sched_setaffinity");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Read messages from child */
|
||||
char buffer[100];
|
||||
for (int i = 0; i < NBR_MSG; i++) {
|
||||
read(fd[1], buffer, strlen(MSG[i]));
|
||||
printf("Message %d: %s\n", i, buffer);
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
}
|
||||
|
||||
} else if (pid > 0) { /* Child process */
|
||||
pid_t child_pid = getpid();
|
||||
printf("Child process: pid=%d\n", child_pid);
|
||||
|
||||
/* Setup CPU affinity for process */
|
||||
CPU_SET(parent_cpu, &set);
|
||||
int ret = sched_setaffinity(child_pid, sizeof(set), &set);
|
||||
if (ret == -1) {
|
||||
perror("sched_setaffinity");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Write messages for the parent process */
|
||||
for (int i = 0; i < NBR_MSG; i++) {
|
||||
write(fd[0], MSG[i], strlen(MSG[i]));
|
||||
}
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
} else {
|
||||
/* error */
|
||||
perror("fork fail");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Test signal handling */
|
||||
kill(getpid(), SIGHUP);
|
||||
kill(getpid(), SIGQUIT);
|
||||
kill(getpid(), SIGTERM);
|
||||
kill(getpid(), SIGABRT);
|
||||
kill(getpid(), SIGINT);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 {init|high|low}"
|
||||
echo " ./shared-cpu.sh init - Initialize cgroup filesystem with cpu.shares groups"
|
||||
echo " . ./shared-cpu.sh high - Run ./max-cpu --single in 'high' cgroup (75% CPU share on CPU 0)"
|
||||
echo " . ./shared-cpu.sh low - Run ./max-cpu --single in 'low' cgroup (25% CPU share on CPU 0)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
# Helper to check if a directory is already mounted
|
||||
is_mounted() {
|
||||
mount | grep -q " $1 "
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
init)
|
||||
echo "Initializing cgroup filesystem for CPU shares..."
|
||||
|
||||
# Mount tmpfs for cgroup if not already mounted
|
||||
if ! is_mounted /sys/fs/cgroup; then
|
||||
echo "Mounting /sys/fs/cgroup..."
|
||||
mount -t tmpfs none /sys/fs/cgroup || {
|
||||
echo "ERROR: Failed to mount /sys/fs/cgroup"
|
||||
exit 1
|
||||
}
|
||||
else
|
||||
echo "/sys/fs/cgroup is already mounted."
|
||||
fi
|
||||
|
||||
# Create and mount cpuset cgroup if not already mounted
|
||||
mkdir -p /sys/fs/cgroup/cpuset
|
||||
if ! is_mounted /sys/fs/cgroup/cpuset; then
|
||||
echo "Mounting cpuset/cpu cgroup..."
|
||||
mount -t cgroup -o cpu,cpuset cpuset /sys/fs/cgroup/cpuset || {
|
||||
echo "ERROR: Failed to mount cpuset cgroup! Perhaps 'cpu' and 'cpuset' are already mounted separately elsewhere?"
|
||||
exit 1
|
||||
}
|
||||
else
|
||||
echo "/sys/fs/cgroup/cpuset is already mounted."
|
||||
fi
|
||||
|
||||
# Create "low" cgroup with 25% share
|
||||
echo "Configuring 'low' cgroup..."
|
||||
mkdir -p /sys/fs/cgroup/cpuset/low
|
||||
echo 0 > /sys/fs/cgroup/cpuset/low/cpuset.cpus || echo "WARNING: Failed to write cpuset.cpus for low cgroup"
|
||||
echo 0 > /sys/fs/cgroup/cpuset/low/cpuset.mems || echo "WARNING: Failed to write cpuset.mems for low cgroup"
|
||||
echo 25 > /sys/fs/cgroup/cpuset/low/cpu.shares || echo "WARNING: Failed to write cpu.shares for low cgroup"
|
||||
|
||||
# Create "high" cgroup with 75% share
|
||||
echo "Configuring 'high' cgroup..."
|
||||
mkdir -p /sys/fs/cgroup/cpuset/high
|
||||
echo 0 > /sys/fs/cgroup/cpuset/high/cpuset.cpus || echo "WARNING: Failed to write cpuset.cpus for high cgroup"
|
||||
echo 0 > /sys/fs/cgroup/cpuset/high/cpuset.mems || echo "WARNING: Failed to write cpuset.mems for high cgroup"
|
||||
echo 75 > /sys/fs/cgroup/cpuset/high/cpu.shares || echo "WARNING: Failed to write cpu.shares for high cgroup"
|
||||
|
||||
echo "Cgroup initialization complete."
|
||||
echo " - low group: 25 shares (25% CPU on CPU 0)"
|
||||
echo " - high group: 75 shares (75% CPU on CPU 0)"
|
||||
;;
|
||||
|
||||
high)
|
||||
echo "Running ./max-cpu --single in 'high' cgroup (75% CPU share)..."
|
||||
if [ ! -f /sys/fs/cgroup/cpuset/high/tasks ]; then
|
||||
echo "ERROR: Cgroup is not initialized or mounted! Run './shared-cpu.sh init' first."
|
||||
exit 1
|
||||
fi
|
||||
echo $$ > /sys/fs/cgroup/cpuset/high/tasks || {
|
||||
echo "ERROR: Failed to add shell process ($$) to high cgroup!"
|
||||
exit 1
|
||||
}
|
||||
./max-cpu --single
|
||||
;;
|
||||
|
||||
low)
|
||||
echo "Running ./max-cpu --single in 'low' cgroup (25% CPU share)..."
|
||||
if [ ! -f /sys/fs/cgroup/cpuset/low/tasks ]; then
|
||||
echo "ERROR: Cgroup is not initialized or mounted! Run './shared-cpu.sh init' first."
|
||||
exit 1
|
||||
fi
|
||||
echo $$ > /sys/fs/cgroup/cpuset/low/tasks || {
|
||||
echo "ERROR: Failed to add shell process ($$) to low cgroup!"
|
||||
exit 1
|
||||
}
|
||||
./max-cpu --single
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown command: $1"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
@@ -1,5 +0,0 @@
|
||||
DIRS=$(filter-out Makefile, $(wildcard *))
|
||||
|
||||
all clean install clean_all:
|
||||
for dir in $(DIRS); do $(MAKE) $@ -C $$dir; done
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
EXE=clock
|
||||
SRCS=$(wildcard *.c)
|
||||
|
||||
ifeq ($(target),)
|
||||
target=nano
|
||||
endif
|
||||
|
||||
CFLAGS=-Wall -Wextra -g -c -O0 -MD -std=gnu11 -D_GNU_SOURCE
|
||||
|
||||
ifeq ($(target),nano)
|
||||
TOOLCHAIN_PATH=/buildroot/output/host/usr/bin/
|
||||
TOOLCHAIN=$(TOOLCHAIN_PATH)aarch64-linux-
|
||||
CFLAGS+=-mcpu=cortex-a53 -funwind-tables
|
||||
##CFLAGS+=-O2 -fno-omit-frame-pointer
|
||||
OBJDIR=.obj/nano
|
||||
EXEC=$(EXE)
|
||||
endif
|
||||
|
||||
ifeq ($(target),host)
|
||||
EXEC=$(EXE)_h
|
||||
endif
|
||||
|
||||
CC=$(TOOLCHAIN)gcc
|
||||
LD=$(TOOLCHAIN)gcc
|
||||
AR=$(TOOLCHAIN)ar
|
||||
STRIP=$(TOOLCHAIN)strip
|
||||
OBJDUMP=$(TOOLCHAIN)objdump
|
||||
|
||||
OBJDIR=.obj/$(target)
|
||||
OBJS= $(addprefix $(OBJDIR)/, $(SRCS:.c=.o))
|
||||
|
||||
$(OBJDIR)/%o: %c
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
all: $(OBJDIR)/ $(EXEC)
|
||||
|
||||
$(EXEC): $(OBJS) $(LINKER_SCRIPT)
|
||||
$(LD) $(OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
$(OBJDIR)/:
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
clean:
|
||||
rm -Rf $(OBJDIR) $(EXEC) $(EXEC)_s *~ t.txt
|
||||
|
||||
clean_all: clean
|
||||
rm -Rf .obj $(EXE) $(EXE)_s $(EXE)_a $(EXE)_a_s $(EXE)_h $(EXE)_h_s
|
||||
|
||||
dump: all
|
||||
$(OBJDUMP) -dS $(EXEC) > t.txt
|
||||
|
||||
-include $(OBJS:.o=.d)
|
||||
|
||||
.PHONY: all clean clean_all dump
|
||||
@@ -1,56 +0,0 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
void measure (int mode, int samples)
|
||||
{
|
||||
struct timespec start_time;
|
||||
struct timespec stop_time;
|
||||
clock_gettime (mode, &start_time); // setup...
|
||||
clock_gettime (mode, &start_time);
|
||||
for (int i = 0; i<samples; i++)
|
||||
{
|
||||
clock_gettime (mode, &start_time);
|
||||
clock_gettime (mode, &stop_time);
|
||||
long long t = (stop_time.tv_nsec - start_time.tv_nsec) +
|
||||
(stop_time.tv_sec - start_time.tv_sec) * 1000000000;
|
||||
printf ("%lld\n", t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* main program...
|
||||
*/
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
cpu_set_t my_set;
|
||||
CPU_ZERO(&my_set);
|
||||
CPU_SET(2, &my_set);
|
||||
sched_setaffinity(0, sizeof(cpu_set_t), &my_set);
|
||||
|
||||
printf ("clocks_per_sec=%ld\n", CLOCKS_PER_SEC);
|
||||
|
||||
int samples = 1000;
|
||||
if (argc == 2)
|
||||
samples = atol(argv[1]);
|
||||
|
||||
struct timespec start_time;
|
||||
clock_getres (CLOCK_MONOTONIC_RAW, &start_time);
|
||||
long t = start_time.tv_sec * 1000000000 + start_time.tv_nsec;
|
||||
printf ("time=%ld'%03ld'%03ld ns\n", t/1000000, (t/1000)%1000, t%1000);
|
||||
|
||||
measure (CLOCK_MONOTONIC_RAW, samples);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
EXE_BASIC=basic
|
||||
EXE_OPTI=optimized
|
||||
|
||||
SRCS_BASIC=basic.c
|
||||
SRCS_OPTI=optimized.c
|
||||
|
||||
ifeq ($(target),)
|
||||
target=nano
|
||||
endif
|
||||
|
||||
CFLAGS=-Wall -Wextra -g -c -O1 -MD -std=gnu11 -D_GNU_SOURCE
|
||||
|
||||
ifeq ($(target),nano)
|
||||
TOOLCHAIN_PATH=/buildroot/output/host/usr/bin/
|
||||
TOOLCHAIN=$(TOOLCHAIN_PATH)aarch64-linux-
|
||||
CFLAGS+=-mcpu=cortex-a53 -funwind-tables
|
||||
CFLAGS+=-fno-omit-frame-pointer
|
||||
##CFLAGS+=-O2
|
||||
EXEC_SUFFIX=
|
||||
endif
|
||||
|
||||
ifeq ($(target),host)
|
||||
TOOLCHAIN=
|
||||
EXEC_SUFFIX=_h
|
||||
endif
|
||||
|
||||
CC=$(TOOLCHAIN)gcc
|
||||
LD=$(TOOLCHAIN)gcc
|
||||
AR=$(TOOLCHAIN)ar
|
||||
STRIP=$(TOOLCHAIN)strip
|
||||
OBJDUMP=$(TOOLCHAIN)objdump
|
||||
|
||||
OBJDIR=.obj/$(target)
|
||||
|
||||
OBJS_BASIC = $(addprefix $(OBJDIR)/, $(SRCS_BASIC:.c=.o))
|
||||
OBJS_OPTI = $(addprefix $(OBJDIR)/, $(SRCS_OPTI:.c=.o))
|
||||
|
||||
EXEC_BASIC = $(EXE_BASIC)$(EXEC_SUFFIX)
|
||||
EXEC_OPTI = $(EXE_OPTI)$(EXEC_SUFFIX)
|
||||
|
||||
|
||||
all: $(EXEC_BASIC) $(EXEC_OPTI)
|
||||
|
||||
basic: $(EXEC_BASIC)
|
||||
opti: $(EXEC_OPTI)
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
$(EXEC_BASIC): $(OBJS_BASIC)
|
||||
$(LD) $(OBJS_BASIC) $(LDFLAGS) -o $@
|
||||
|
||||
$(EXEC_OPTI): $(OBJS_OPTI)
|
||||
$(LD) $(OBJS_OPTI) $(LDFLAGS) -o $@
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
|
||||
clean:
|
||||
rm -Rf $(OBJDIR) $(EXEC_BASIC) $(EXEC_OPTI) *~ t_*.txt
|
||||
|
||||
clean_all: clean
|
||||
rm -Rf .obj $(EXE_BASIC)* $(EXE_OPTI)*
|
||||
|
||||
dump_basic: $(EXEC_BASIC)
|
||||
$(OBJDUMP) -dS $(EXEC_BASIC) > t_basic.txt
|
||||
|
||||
dump_opti: $(EXEC_OPTI)
|
||||
$(OBJDUMP) -dS $(EXEC_OPTI) > t_opti.txt
|
||||
|
||||
-include $(OBJS_BASIC:.o=.d) $(OBJS_OPTI:.o=.d)
|
||||
|
||||
.PHONY: all basic opti clean clean_all dump_basic dump_opti
|
||||
@@ -1,23 +0,0 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#define SIZE 5000
|
||||
|
||||
static int32_t array[SIZE][SIZE];
|
||||
|
||||
int main (void)
|
||||
{
|
||||
int i, j, k;
|
||||
|
||||
for (k = 0; k < 10; k++)
|
||||
{
|
||||
for (i = 0; i < SIZE; i++)
|
||||
{
|
||||
for (j = 0; j < SIZE; j++)
|
||||
{
|
||||
array[j][i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#define SIZE 5000
|
||||
|
||||
static int32_t array[SIZE][SIZE];
|
||||
|
||||
int main (void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
|
||||
for (i = 0; i < SIZE; i++)
|
||||
{
|
||||
for (j = 0; j < SIZE; j++)
|
||||
{
|
||||
array[i][j]+= 10;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
EXE_BASIC=basic
|
||||
EXE_OPTI=optimized
|
||||
|
||||
SRCS_BASIC=basic.c
|
||||
SRCS_OPTI=optimized.c
|
||||
|
||||
ifeq ($(target),)
|
||||
target=nano
|
||||
endif
|
||||
|
||||
CFLAGS=-Wall -Wextra -g -c -O0 -MD -std=gnu11 -D_GNU_SOURCE
|
||||
|
||||
ifeq ($(target),nano)
|
||||
TOOLCHAIN_PATH=/buildroot/output/host/usr/bin/
|
||||
TOOLCHAIN=$(TOOLCHAIN_PATH)aarch64-linux-
|
||||
CFLAGS+=-mcpu=cortex-a53 -funwind-tables
|
||||
CFLAGS+=-fno-omit-frame-pointer
|
||||
#CFLAGS+=-O2
|
||||
EXEC_SUFFIX=
|
||||
endif
|
||||
|
||||
ifeq ($(target),host)
|
||||
TOOLCHAIN=
|
||||
EXEC_SUFFIX=_h
|
||||
endif
|
||||
|
||||
CC=$(TOOLCHAIN)gcc
|
||||
LD=$(TOOLCHAIN)gcc
|
||||
AR=$(TOOLCHAIN)ar
|
||||
STRIP=$(TOOLCHAIN)strip
|
||||
OBJDUMP=$(TOOLCHAIN)objdump
|
||||
|
||||
OBJDIR=.obj/$(target)
|
||||
|
||||
OBJS_BASIC = $(addprefix $(OBJDIR)/, $(SRCS_BASIC:.c=.o))
|
||||
OBJS_OPTI = $(addprefix $(OBJDIR)/, $(SRCS_OPTI:.c=.o))
|
||||
|
||||
EXEC_BASIC = $(EXE_BASIC)$(EXEC_SUFFIX)
|
||||
EXEC_OPTI = $(EXE_OPTI)$(EXEC_SUFFIX)
|
||||
|
||||
|
||||
all: $(EXEC_BASIC) $(EXEC_OPTI)
|
||||
|
||||
basic: $(EXEC_BASIC)
|
||||
opti: $(EXEC_OPTI)
|
||||
|
||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
$(EXEC_BASIC): $(OBJS_BASIC)
|
||||
$(LD) $(OBJS_BASIC) $(LDFLAGS) -o $@
|
||||
|
||||
$(EXEC_OPTI): $(OBJS_OPTI)
|
||||
$(LD) $(OBJS_OPTI) $(LDFLAGS) -o $@
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
|
||||
clean:
|
||||
rm -Rf $(OBJDIR) $(EXEC_BASIC) $(EXEC_OPTI) *~ t_*.txt
|
||||
|
||||
clean_all: clean
|
||||
rm -Rf .obj $(EXE_BASIC)* $(EXE_OPTI)*
|
||||
|
||||
dump_basic: $(EXEC_BASIC)
|
||||
$(OBJDUMP) -dS $(EXEC_BASIC) > t_basic.txt
|
||||
|
||||
dump_opti: $(EXEC_OPTI)
|
||||
$(OBJDUMP) -dS $(EXEC_OPTI) > t_opti.txt
|
||||
|
||||
-include $(OBJS_BASIC:.o=.d) $(OBJS_OPTI:.o=.d)
|
||||
|
||||
.PHONY: all basic opti clean clean_all dump_basic dump_opti
|
||||
@@ -1,24 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define SIZE 65536
|
||||
|
||||
int main()
|
||||
{
|
||||
// generate data
|
||||
short data[SIZE];
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
data[i] = rand() % 512;
|
||||
}
|
||||
|
||||
|
||||
long long sum = 0;
|
||||
for (int j = 0; j < 10000; j++) {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
if (data[i] >= 256) {
|
||||
sum += data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
printf ("sum=%lld\n", sum);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define SIZE 65536
|
||||
|
||||
static int compare (const void* a, const void* b)
|
||||
{
|
||||
return *(short*)a - *(short*)b;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// generate data
|
||||
short data[SIZE];
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
data[i] = rand() % 512;
|
||||
}
|
||||
|
||||
qsort(data, SIZE, sizeof(data[0]), compare);
|
||||
|
||||
|
||||
long long sum = 0;
|
||||
for (int j = 0; j < 10000; j++) {
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
if (data[i] >= 256) {
|
||||
sum += data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
printf ("sum=%lld\n", sum);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#include "ApacheAccessLogAnalyzer.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
ApacheAccessLogAnalyzer::ApacheAccessLogAnalyzer(std::string filename)
|
||||
: myFilename(filename)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ApacheAccessLogAnalyzer::openFile()
|
||||
{
|
||||
myInFile.open(myFilename.c_str());
|
||||
}
|
||||
|
||||
void ApacheAccessLogAnalyzer::closeFile()
|
||||
{
|
||||
myInFile.close();
|
||||
}
|
||||
|
||||
void ApacheAccessLogAnalyzer::processFile()
|
||||
{
|
||||
std::cout << "Processing log file " << myFilename << std::endl;
|
||||
for( std::string line; getline( myInFile, line ); )
|
||||
{
|
||||
// parse the log line to extract the hostname / ip address
|
||||
int space_pos = line.find_first_of(" ");
|
||||
std::string hostname = line.substr(0, space_pos);
|
||||
|
||||
myHostCounter.notifyHost(hostname);
|
||||
}
|
||||
|
||||
std::cout << "Found " << myHostCounter.getNbOfHosts() << " unique Hosts/IPs" << std::endl;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#include "HostCounter.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
class ApacheAccessLogAnalyzer
|
||||
{
|
||||
public:
|
||||
ApacheAccessLogAnalyzer(std::string filename);
|
||||
|
||||
void openFile();
|
||||
void closeFile();
|
||||
void processFile();
|
||||
|
||||
private:
|
||||
std::string myFilename;
|
||||
std::ifstream myInFile;
|
||||
HostCounter myHostCounter;
|
||||
};
|
||||
@@ -1,26 +0,0 @@
|
||||
#include "HostCounter.h"
|
||||
|
||||
#include <algorithm> // for std::find
|
||||
|
||||
HostCounter::HostCounter()
|
||||
{
|
||||
}
|
||||
|
||||
bool HostCounter::isNewHost(std::string hostname)
|
||||
{
|
||||
return myHosts.find(hostname) == myHosts.end();
|
||||
}
|
||||
|
||||
void HostCounter::notifyHost(std::string hostname)
|
||||
{
|
||||
// add the host in the list if not already in
|
||||
if(isNewHost(hostname))
|
||||
{
|
||||
myHosts.insert(hostname);
|
||||
}
|
||||
}
|
||||
|
||||
int HostCounter::getNbOfHosts()
|
||||
{
|
||||
return myHosts.size();
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#include <string>
|
||||
|
||||
#include <set>
|
||||
|
||||
class HostCounter
|
||||
{
|
||||
public:
|
||||
HostCounter();
|
||||
|
||||
// Announce a host to the HostCounter.
|
||||
// if the host is new, it will be added to the list, otherwise we ignore it.
|
||||
void notifyHost(std::string hostname);
|
||||
|
||||
// return the number of unique hosts found so far
|
||||
int getNbOfHosts();
|
||||
|
||||
private:
|
||||
// check if host is already in the list
|
||||
bool isNewHost(std::string hostname);
|
||||
|
||||
std::set< std::string > myHosts;
|
||||
};
|
||||
@@ -1,30 +0,0 @@
|
||||
##CXX?=g++
|
||||
##CXXFLAGS=-Wall -Wextra -g -O0 -MD
|
||||
|
||||
TOOLCHAIN_PATH=/buildroot/output/host/usr/bin/
|
||||
TOOLCHAIN=$(TOOLCHAIN_PATH)aarch64-linux-
|
||||
CXX=$(TOOLCHAIN)g++
|
||||
CXXFLAGS=-Wall -Wextra -g -gdwarf -O0 -MD -mcpu=cortex-a53 -fno-omit-frame-pointer -funwind-tables
|
||||
|
||||
SOURCES=$(wildcard *.cpp)
|
||||
OBJECTS=$(SOURCES:.cpp=.o)
|
||||
EXECUTABLE=read-apache-logs
|
||||
|
||||
all: $(SOURCES) $(EXECUTABLE)
|
||||
|
||||
$(EXECUTABLE): $(OBJECTS)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $(OBJECTS)
|
||||
|
||||
.c.o:
|
||||
$(CXX) -c $(CXXFLAGS) $< -o $@
|
||||
|
||||
|
||||
clean:
|
||||
@rm -f $(OBJECTS)
|
||||
@rm -f *.d *~
|
||||
|
||||
clean_all: clean
|
||||
@rm -f $(EXECUTABLE)
|
||||
@rm -f perf.data perf.data.old
|
||||
|
||||
-include *.d
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:96551161b5bdcaacbc3c17fa108191c478fb35dfe87895c16e34a8f6552bf29a
|
||||
size 205242368
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3b9e10566fd24f42f7631c0ab9f1159cda668f11a4f4f375e716f2566b00d0a7
|
||||
size 22005531
|
||||
@@ -1,30 +0,0 @@
|
||||
#include "ApacheAccessLogAnalyzer.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// forward declaration
|
||||
void usage(const char* progName);
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
if(argc != 2)
|
||||
{
|
||||
usage(argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string filename = argv[1];
|
||||
|
||||
ApacheAccessLogAnalyzer analyzer(filename);
|
||||
|
||||
analyzer.openFile();
|
||||
analyzer.processFile();
|
||||
analyzer.closeFile();
|
||||
}
|
||||
|
||||
|
||||
void usage(const char* progName)
|
||||
{
|
||||
std::cout << "Usage: " << progName << " <filename>" << std::endl;
|
||||
std::cout << "\nWhere <filename> is the apache access log file" << std::endl;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
CC?=gcc
|
||||
CFLAGS=-std=c11 -Wall -g -fprofile-arcs -ftest-coverage
|
||||
SOURCES=$(wildcard *.c)
|
||||
OBJECTS=$(SOURCES:.c=.o)
|
||||
EXECUTABLE=example-gcov
|
||||
|
||||
all: $(SOURCES) $(EXECUTABLE)
|
||||
|
||||
$(EXECUTABLE): $(OBJECTS)
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJECTS)
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
|
||||
clean:
|
||||
@rm -f $(OBJECTS)
|
||||
@rm -f *.d
|
||||
|
||||
clean_all: clean
|
||||
@rm -f $(EXECUTABLE)
|
||||
@rm -f perf.data perf.data.old
|
||||
@rm -f *.gcno *.gcda *.gcov
|
||||
|
||||
|
||||
gcov-generate: all
|
||||
./$(EXECUTABLE)
|
||||
gcov main.c
|
||||
|
||||
gcov-read: gcov-generate
|
||||
less main.c.gcov
|
||||
|
||||
|
||||
-include *.d
|
||||
@@ -1,19 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
int res = 1;
|
||||
for (int i=0; i<16; i++)
|
||||
{
|
||||
res *= 2;
|
||||
}
|
||||
|
||||
if(res < 10)
|
||||
{
|
||||
printf("dead code\n");
|
||||
}
|
||||
|
||||
printf("res=%d\n", res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
EXE=gpio
|
||||
SRCS=$(wildcard *.c)
|
||||
|
||||
ifeq ($(target),)
|
||||
target=nano
|
||||
endif
|
||||
|
||||
CFLAGS=-Wall -Wextra -g -c -O1 -MD -std=gnu11 -D_GNU_SOURCE
|
||||
|
||||
ifeq ($(target),nano)
|
||||
TOOLCHAIN_PATH=/buildroot/output/host/usr/bin/
|
||||
TOOLCHAIN=$(TOOLCHAIN_PATH)aarch64-linux-
|
||||
CFLAGS+=-mcpu=cortex-a53 -funwind-tables
|
||||
##CFLAGS+=-O2 -fno-omit-frame-pointer
|
||||
OBJDIR=.obj/nano
|
||||
EXEC=$(EXE)
|
||||
endif
|
||||
|
||||
ifeq ($(target),host)
|
||||
EXEC=$(EXE)_h
|
||||
endif
|
||||
|
||||
CC=$(TOOLCHAIN)gcc
|
||||
LD=$(TOOLCHAIN)gcc
|
||||
AR=$(TOOLCHAIN)ar
|
||||
STRIP=$(TOOLCHAIN)strip
|
||||
OBJDUMP=$(TOOLCHAIN)objdump
|
||||
|
||||
OBJDIR=.obj/$(target)
|
||||
OBJS= $(addprefix $(OBJDIR)/, $(SRCS:.c=.o))
|
||||
|
||||
$(OBJDIR)/%o: %c
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
all: $(OBJDIR)/ $(EXEC)
|
||||
|
||||
$(EXEC): $(OBJS) $(LINKER_SCRIPT)
|
||||
$(LD) $(OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
$(OBJDIR)/:
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
clean:
|
||||
rm -Rf $(OBJDIR) $(EXEC) $(EXEC)_s *~ t.txt
|
||||
|
||||
clean_all: clean
|
||||
rm -Rf .obj $(EXE) $(EXE)_s $(EXE)_a $(EXE)_a_s $(EXE)_h $(EXE)_h_s
|
||||
|
||||
dump: all
|
||||
$(OBJDUMP) -dS $(EXEC) > t.txt
|
||||
|
||||
-include $(OBJS:.o=.d)
|
||||
|
||||
.PHONY: all clean clean_all dump
|
||||
@@ -1,87 +0,0 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <sched.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define GPIO_EXPORT "/sys/class/gpio/export"
|
||||
#define GPIO_UNEXPORT "/sys/class/gpio/unexport"
|
||||
#define GPIO_GPIOG11 "/sys/class/gpio/gpio203"
|
||||
#define GPIOG11 "203"
|
||||
|
||||
static int open_gpio()
|
||||
{
|
||||
// unexport pin out of sysfs (reinitialization)
|
||||
int f = open (GPIO_UNEXPORT, O_WRONLY);
|
||||
write (f, GPIOG11, strlen(GPIOG11));
|
||||
close (f);
|
||||
|
||||
// export pin to sysfs
|
||||
f = open (GPIO_EXPORT, O_WRONLY);
|
||||
write (f, GPIOG11, strlen(GPIOG11));
|
||||
close (f);
|
||||
|
||||
// config pin
|
||||
f = open (GPIO_GPIOG11 "/direction", O_WRONLY);
|
||||
write (f, "out", 3);
|
||||
close (f);
|
||||
|
||||
// open gpio value attribute
|
||||
f = open (GPIO_GPIOG11 "/value", O_RDWR);
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* main program...
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
cpu_set_t my_set;
|
||||
CPU_ZERO(&my_set);
|
||||
CPU_SET(2, &my_set);
|
||||
sched_setaffinity(0, sizeof(cpu_set_t), &my_set);
|
||||
|
||||
int gpio = open_gpio();
|
||||
bool on = false;
|
||||
struct timespec start_time;
|
||||
struct timespec stop_time;
|
||||
clock_gettime (CLOCK_MONOTONIC, &start_time); // setup...
|
||||
|
||||
|
||||
// --> measurement with internal timers
|
||||
clock_gettime (CLOCK_MONOTONIC, &start_time);
|
||||
for (int i=0; i<1000; i++) {
|
||||
if (on) {
|
||||
pwrite (gpio, "1", sizeof("1"), 0);
|
||||
} else {
|
||||
pwrite (gpio, "0", sizeof("0"), 0);
|
||||
}
|
||||
on = !on;
|
||||
}
|
||||
clock_gettime (CLOCK_MONOTONIC, &stop_time);
|
||||
long long t = (stop_time.tv_nsec - start_time.tv_nsec) +
|
||||
(stop_time.tv_sec - start_time.tv_sec) * 1000000000;
|
||||
printf ("pwrite (gpio, \"1\", sizeof(\"1\"), 0) -> %lld ns\n", t/1000);
|
||||
|
||||
|
||||
// --> measurement with oscilloscope
|
||||
while(true) {
|
||||
if (on) {
|
||||
pwrite (gpio, "1", sizeof("1"), 0);
|
||||
} else {
|
||||
pwrite (gpio, "0", sizeof("0"), 0);
|
||||
}
|
||||
on = !on;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
CC?=gcc
|
||||
CFLAGS=-std=c11 -Wall -pg
|
||||
SOURCES=$(wildcard *.c)
|
||||
OBJECTS=$(SOURCES:.c=.o)
|
||||
EXECUTABLE=example-gprof
|
||||
|
||||
all: $(SOURCES) $(EXECUTABLE)
|
||||
|
||||
$(EXECUTABLE): $(OBJECTS)
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJECTS)
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
|
||||
clean:
|
||||
@rm -f $(OBJECTS)
|
||||
@rm -f *.d
|
||||
|
||||
clean_all: clean
|
||||
@rm -f $(EXECUTABLE)
|
||||
@rm -f perf.data perf.data.old
|
||||
@rm -f gmon.out gprof.out
|
||||
|
||||
|
||||
gprof-generate: all
|
||||
./$(EXECUTABLE)
|
||||
gprof $(EXECUTABLE) > gprof.out
|
||||
|
||||
gprof-read:
|
||||
less gprof.out
|
||||
|
||||
|
||||
-include *.d
|
||||
@@ -1,21 +0,0 @@
|
||||
void func1(void)
|
||||
{
|
||||
for(int i=0; i<0xfffffff; i++); // wait...
|
||||
return;
|
||||
}
|
||||
|
||||
void func2(void)
|
||||
{
|
||||
for(int i=0;i<0xfffffff;i++); // wait...
|
||||
func1();
|
||||
return;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
for(int i=0;i<0xfffffff;i++);
|
||||
func1();
|
||||
func2();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
EXE=example-gprof
|
||||
SRCS=$(wildcard *.c)
|
||||
|
||||
ifeq ($(target),)
|
||||
target=nano
|
||||
endif
|
||||
|
||||
CFLAGS=-Wall -Wextra -g -c -O1 -MD -std=gnu11 -D_GNU_SOURCE -pg
|
||||
|
||||
ifeq ($(target),nano)
|
||||
TOOLCHAIN_PATH=/buildroot/output/host/usr/bin/
|
||||
TOOLCHAIN=$(TOOLCHAIN_PATH)aarch64-linux-
|
||||
CFLAGS+=-mcpu=cortex-a53 -funwind-tables
|
||||
CFLAGS+=-O2 -fno-omit-frame-pointer
|
||||
OBJDIR=.obj/nano
|
||||
EXEC=$(EXE)
|
||||
endif
|
||||
|
||||
ifeq ($(target),host)
|
||||
EXEC=$(EXE)_h
|
||||
endif
|
||||
|
||||
ifeq ($(target),xu3)
|
||||
TOOLCHAIN_PATH=~/workspace/xu3/buildroot/output/host/usr/bin/
|
||||
TOOLCHAIN=$(TOOLCHAIN_PATH)arm-linux-gnueabihf-
|
||||
CFLAGS+=-mcpu=cortex-a15.cortex-a7 -funwind-tables
|
||||
##CFLAGS+=-O2 -fno-omit-frame-pointer
|
||||
OBJDIR=.obj/xu3
|
||||
EXEC=$(EXE)_a
|
||||
endif
|
||||
|
||||
CC=$(TOOLCHAIN)gcc
|
||||
LD=$(TOOLCHAIN)gcc
|
||||
AR=$(TOOLCHAIN)ar
|
||||
STRIP=$(TOOLCHAIN)strip
|
||||
OBJDUMP=$(TOOLCHAIN)objdump
|
||||
|
||||
OBJDIR=.obj/$(target)
|
||||
OBJS= $(addprefix $(OBJDIR)/, $(SRCS:.c=.o))
|
||||
|
||||
$(OBJDIR)/%o: %c
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
all: $(OBJDIR)/ $(EXEC)
|
||||
|
||||
$(EXEC): $(OBJS) $(LINKER_SCRIPT)
|
||||
$(LD) $(OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
$(OBJDIR)/:
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
clean:
|
||||
rm -Rf $(OBJDIR) $(EXEC) $(EXEC)_s *~ t.txt
|
||||
|
||||
clean_all: clean
|
||||
rm -Rf .obj $(EXE) $(EXE)_s $(EXE)_a $(EXE)_a_s $(EXE)_h $(EXE)_h_s
|
||||
@rm -f perf.data perf.data.old gmon.out gprof.out
|
||||
|
||||
dump: all
|
||||
$(OBJDUMP) -dS $(EXEC) > t.txt
|
||||
|
||||
gprof-generate: all
|
||||
./$(EXECUTABLE)
|
||||
gprof $(EXECUTABLE) > gprof.out
|
||||
|
||||
gprof-read:
|
||||
less gprof.out
|
||||
|
||||
-include $(OBJS:.o=.d)
|
||||
|
||||
.PHONY: all clean clean_all dump
|
||||
@@ -1,54 +0,0 @@
|
||||
EXE=mmio
|
||||
SRCS=$(wildcard *.c)
|
||||
|
||||
ifeq ($(target),)
|
||||
target=nano
|
||||
endif
|
||||
|
||||
CFLAGS=-Wall -Wextra -g -c -O1 -MD -std=gnu11 -D_GNU_SOURCE
|
||||
|
||||
ifeq ($(target),nano)
|
||||
TOOLCHAIN_PATH=/buildroot/output/host/usr/bin/
|
||||
TOOLCHAIN=$(TOOLCHAIN_PATH)aarch64-linux-
|
||||
CFLAGS+=-mcpu=cortex-a53 -funwind-tables
|
||||
##CFLAGS+=-O2 -fno-omit-frame-pointer
|
||||
OBJDIR=.obj/nano
|
||||
EXEC=$(EXE)
|
||||
endif
|
||||
|
||||
ifeq ($(target),host)
|
||||
EXEC=$(EXE)_h
|
||||
endif
|
||||
|
||||
CC=$(TOOLCHAIN)gcc
|
||||
LD=$(TOOLCHAIN)gcc
|
||||
AR=$(TOOLCHAIN)ar
|
||||
STRIP=$(TOOLCHAIN)strip
|
||||
OBJDUMP=$(TOOLCHAIN)objdump
|
||||
|
||||
OBJDIR=.obj/$(target)
|
||||
OBJS= $(addprefix $(OBJDIR)/, $(SRCS:.c=.o))
|
||||
|
||||
$(OBJDIR)/%o: %c
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
all: $(OBJDIR)/ $(EXEC)
|
||||
|
||||
$(EXEC): $(OBJS) $(LINKER_SCRIPT)
|
||||
$(LD) $(OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
$(OBJDIR)/:
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
clean:
|
||||
rm -Rf $(OBJDIR) $(EXEC) $(EXEC)_s *~ t.txt
|
||||
|
||||
clean_all: clean
|
||||
rm -Rf .obj $(EXE) $(EXE)_s $(EXE)_a $(EXE)_a_s $(EXE)_h $(EXE)_h_s
|
||||
|
||||
dump: all
|
||||
$(OBJDUMP) -dS $(EXEC) > t.txt
|
||||
|
||||
-include $(OBJS:.o=.d)
|
||||
|
||||
.PHONY: all clean clean_all dump
|
||||
@@ -1,98 +0,0 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <sched.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define GPIO_EXPORT "/sys/class/gpio/export"
|
||||
#define GPIO_UNEXPORT "/sys/class/gpio/unexport"
|
||||
#define GPIO_GPIOG11 "/sys/class/gpio/gpio203"
|
||||
#define GPIOG11 "203"
|
||||
|
||||
static int open_gpio()
|
||||
{
|
||||
// unexport pin out of sysfs (reinitialization)
|
||||
int f = open (GPIO_UNEXPORT, O_WRONLY);
|
||||
write (f, GPIOG11, strlen(GPIOG11));
|
||||
close (f);
|
||||
|
||||
// export pin to sysfs
|
||||
f = open (GPIO_EXPORT, O_WRONLY);
|
||||
write (f, GPIOG11, strlen(GPIOG11));
|
||||
close (f);
|
||||
|
||||
// config pin
|
||||
f = open (GPIO_GPIOG11 "/direction", O_WRONLY);
|
||||
write (f, "out", 3);
|
||||
close (f);
|
||||
|
||||
// open gpio value attribute
|
||||
f = open (GPIO_GPIOG11 "/value", O_RDWR);
|
||||
return f;
|
||||
}
|
||||
|
||||
/**
|
||||
* main program...
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
cpu_set_t my_set;
|
||||
CPU_ZERO(&my_set);
|
||||
CPU_SET(2, &my_set);
|
||||
sched_setaffinity(0, sizeof(cpu_set_t), &my_set);
|
||||
|
||||
int gpio = open_gpio();
|
||||
pwrite (gpio, "1", sizeof("1"), 0);
|
||||
pwrite (gpio, "0", sizeof("0"), 0);
|
||||
|
||||
int fd = open ("/dev/mem", O_RDWR);
|
||||
if (fd == -1) return -1;
|
||||
off_t psz = getpagesize();
|
||||
volatile uint32_t* pio = mmap (0, psz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x01c20000);
|
||||
volatile uint32_t* dat_pio = pio + (0x810+6*0x24)/4;
|
||||
|
||||
struct timespec start_time;
|
||||
struct timespec stop_time;
|
||||
clock_gettime (CLOCK_MONOTONIC, &start_time); // setup...
|
||||
|
||||
// --> measurement with internal timers on gpio
|
||||
clock_gettime (CLOCK_MONOTONIC, &start_time);
|
||||
|
||||
for (int i=0; i<1000; i++)
|
||||
*dat_pio ^= (1<<11);
|
||||
|
||||
clock_gettime (CLOCK_MONOTONIC, &stop_time);
|
||||
long long t = (stop_time.tv_nsec - start_time.tv_nsec) +
|
||||
(stop_time.tv_sec - start_time.tv_sec) * 1000000000;
|
||||
printf ("*dat_pio ^= (1<<11) --> %6lld ns\n", t/1000);
|
||||
|
||||
|
||||
// --> measurement on internal data with internal timers
|
||||
volatile uint32_t data = 0;
|
||||
clock_gettime (CLOCK_MONOTONIC, &start_time);
|
||||
|
||||
for (int i=0; i<1000; i++)
|
||||
data ^= (1<<11);
|
||||
|
||||
clock_gettime (CLOCK_MONOTONIC, &stop_time);
|
||||
t = (stop_time.tv_nsec - start_time.tv_nsec) +
|
||||
(stop_time.tv_sec - start_time.tv_sec) * 1000000000;
|
||||
printf ("data ^= (1<<11) --> %6lld ns\n", t/1000);
|
||||
|
||||
|
||||
// --> measurement with oscilloscope
|
||||
while(true) {
|
||||
*dat_pio ^= (1<<11);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
EXE=tracing
|
||||
SRCS=$(wildcard *.c)
|
||||
|
||||
ifeq ($(target),)
|
||||
target=nano
|
||||
endif
|
||||
|
||||
CFLAGS=-Wall -Wextra -g -c -O1 -MD -std=gnu11 -D_GNU_SOURCE
|
||||
|
||||
ifeq ($(target),nano)
|
||||
TOOLCHAIN_PATH=/buildroot/output/host/usr/bin/
|
||||
TOOLCHAIN=$(TOOLCHAIN_PATH)aarch64-linux-
|
||||
CFLAGS+=-mcpu=cortex-a53 -funwind-tables
|
||||
##CFLAGS+=-O2 -fno-omit-frame-pointer
|
||||
OBJDIR=.obj/nano
|
||||
EXEC=$(EXE)
|
||||
endif
|
||||
|
||||
ifeq ($(target),host)
|
||||
EXEC=$(EXE)_h
|
||||
endif
|
||||
|
||||
CC=$(TOOLCHAIN)gcc
|
||||
LD=$(TOOLCHAIN)gcc
|
||||
AR=$(TOOLCHAIN)ar
|
||||
STRIP=$(TOOLCHAIN)strip
|
||||
OBJDUMP=$(TOOLCHAIN)objdump
|
||||
|
||||
OBJDIR=.obj/$(target)
|
||||
OBJS= $(addprefix $(OBJDIR)/, $(SRCS:.c=.o))
|
||||
|
||||
$(OBJDIR)/%o: %c
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
all: $(OBJDIR)/ $(EXEC)
|
||||
|
||||
$(EXEC): $(OBJS) $(LINKER_SCRIPT)
|
||||
$(LD) $(OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
$(OBJDIR)/:
|
||||
mkdir -p $(OBJDIR)
|
||||
|
||||
clean:
|
||||
rm -Rf $(OBJDIR) $(EXEC) $(EXEC)_s *~ t.txt
|
||||
|
||||
clean_all: clean
|
||||
rm -Rf .obj $(EXE) $(EXE)_s $(EXE)_a $(EXE)_a_s $(EXE)_h $(EXE)_h_s
|
||||
|
||||
dump: all
|
||||
$(OBJDUMP) -dS $(EXEC) > t.txt
|
||||
|
||||
-include $(OBJS:.o=.d)
|
||||
|
||||
.PHONY: all clean clean_all dump
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/sh
|
||||
# 1. example
|
||||
echo 1 > /sys/kernel/debug/tracing/events/sched/sched_switch/enable
|
||||
./tracing &
|
||||
pidof tracing > /sys/kernel/debug/tracing/set_event_pid
|
||||
echo 1 > /sys/kernel/debug/tracing/tracing_on ; sleep 2 ; echo 0 > /sys/kernel/debug/tracing/tracing_on
|
||||
trace-cmd show
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/bin/sh
|
||||
# 2. example
|
||||
echo function_graph > /sys/kernel/debug/tracing/current_tracer
|
||||
echo *i2c* > /sys/kernel/debug/tracing/set_ftrace_filter
|
||||
echo 1 > /sys/kernel/debug/tracing/tracing_on
|
||||
i2cdetect -y 0
|
||||
echo 0 > /sys/kernel/debug/tracing/tracing_on
|
||||
trace-cmd show
|
||||
echo nop > /sys/kernel/debug/tracing/current_tracer
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
while(1)
|
||||
sleep(1);
|
||||
return 0;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
mount -t debugfs nodev /sys/kernel/debug
|
||||
@@ -1,7 +0,0 @@
|
||||
CompileFlags:
|
||||
Add:
|
||||
# Architecture and cross-compilation
|
||||
- "--target=aarch64-linux-gnu"
|
||||
|
||||
# Setup sysroot for buildroot
|
||||
- "--sysroot=/buildroot/output/host/aarch64-buildroot-linux-gnu/sysroot"
|
||||
10
src/06-mini-project/.gitignore
vendored
@@ -1,10 +0,0 @@
|
||||
*.o
|
||||
*.ko
|
||||
*.mod
|
||||
*.mod.c
|
||||
*.mod.o
|
||||
*.ko.cmd
|
||||
*.mod.cmd
|
||||
|
||||
daemon/daemon
|
||||
cli/mp
|
||||
@@ -1,7 +0,0 @@
|
||||
CompileFlags:
|
||||
Add:
|
||||
# Architecture and cross-compilation
|
||||
- "--target=aarch64-linux-gnu"
|
||||
|
||||
# Setup sysroot for buildroot
|
||||
- "--sysroot=/buildroot/output/host/aarch64-buildroot-linux-gnu/sysroot"
|
||||
@@ -1,7 +0,0 @@
|
||||
EXE=mp
|
||||
SRCS=$(shell find . -name "*.c")
|
||||
LDFLAGS+=-lpthread
|
||||
EXTRA_CFLAGS+=-I.
|
||||
|
||||
# Include the standard application Makefile for the CSEL1 labs
|
||||
include ../../appl.mk
|
||||
@@ -1,14 +0,0 @@
|
||||
@default:
|
||||
just --list
|
||||
|
||||
@build:
|
||||
make
|
||||
|
||||
@install:
|
||||
install -m 0755 mp /rootfs/usr/bin/mp
|
||||
echo "temp_cli installed successfully in /rootfs/usr/bin/"
|
||||
|
||||
@clean:
|
||||
make clean
|
||||
rm -f -- mp
|
||||
rm -f /rootfs/usr/bin/mp
|
||||
@@ -1,111 +0,0 @@
|
||||
// workspace/src/06-mini-project/cli/main.c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
/* Relative path to your common IPC definitions */
|
||||
#include "../common/common_ipc.h"
|
||||
|
||||
#define MODE "mode"
|
||||
#define PERIOD "period"
|
||||
#define INC "inc"
|
||||
#define DEC "dec"
|
||||
|
||||
|
||||
/**
|
||||
* print_usage() - Display the help menu for the CLI tool.
|
||||
*/
|
||||
static void print_usage(const char *prog_name) {
|
||||
printf("Temperature Regulator CLI\n");
|
||||
printf("Usage:\n");
|
||||
printf(" %s mode <0|1> : Set mode (0 = Manual, 1 = Auto)\n", prog_name);
|
||||
printf(" %s period <ms> : Set period in ms (only works in Manual mode)\n", prog_name);
|
||||
printf(" %s inc : Increase frequency (only works in Manual mode)\n", prog_name);
|
||||
printf(" %s dec : Decrease frequency (only works in Manual mode)\n", prog_name);
|
||||
printf("\nExamples:\n");
|
||||
printf(" %s mode 1 (Switch to automatic mode)\n", prog_name);
|
||||
printf(" %s period 500 (Set period to 500 ms)\n", prog_name);
|
||||
}
|
||||
|
||||
static int process_arguments(int argc, char *argv[], ipc_msg_t *msg) {
|
||||
|
||||
if (strcmp(argv[1], MODE) == 0 && argc == 3) {
|
||||
msg->command = CMD_SET_MODE;
|
||||
msg->value = atoi(argv[2]);
|
||||
if (msg->value != 0 && msg->value != 1) {
|
||||
printf("Error: Mode must be 0 (Manual) or 1 (Auto).\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else if (strcmp(argv[1], PERIOD) == 0 && argc == 3) {
|
||||
msg->command = CMD_SET_PERIOD;
|
||||
msg->value = atoi(argv[2]);
|
||||
if (msg->value <= 0) {
|
||||
printf("Error: Frequency must be a positive integer.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
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], INC) == 0 && argc == 2) {
|
||||
msg->command = CMD_DEC_PERIOD;
|
||||
msg->value = 0; /* Ignored by daemon */
|
||||
}
|
||||
else {
|
||||
printf("Error: Invalid arguments.\n\n");
|
||||
print_usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int client_fd;
|
||||
struct sockaddr_un server_addr;
|
||||
ipc_msg_t msg = {0};
|
||||
|
||||
/* Parse command line arguments */
|
||||
if (argc < 2) {
|
||||
print_usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (process_arguments(argc, argv, &msg) != EXIT_SUCCESS) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Create local Unix Domain Socket (Datagram) */
|
||||
client_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (client_fd < 0) {
|
||||
perror("Error: Socket creation failed");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Prepare the destination address */
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sun_family = AF_UNIX;
|
||||
strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
|
||||
|
||||
/* Send the binary structure to the Daemon */
|
||||
ssize_t sent_bytes = sendto(client_fd, &msg, sizeof(msg), 0,
|
||||
(struct sockaddr *)&server_addr, sizeof(server_addr));
|
||||
|
||||
if (sent_bytes < 0) {
|
||||
perror("Error: Failed to send message");
|
||||
printf("Hint: Is the background daemon running and listening on '%s'?\n", SOCKET_PATH);
|
||||
close(client_fd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* 5. Cleanup */
|
||||
printf("Success: Command sent to the daemon.\n");
|
||||
close(client_fd);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// common_ipc.h
|
||||
#ifndef COMMON_IPC_H
|
||||
#define COMMON_IPC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#define SOCKET_PATH "/tmp/regulator_daemon.sock"
|
||||
|
||||
/* Commands definitions */
|
||||
#define CMD_SET_MODE 10
|
||||
#define CMD_SET_PERIOD 20
|
||||
#define CMD_INC_PERIOD 30
|
||||
#define CMD_DEC_PERIOD 40
|
||||
|
||||
/* Structure of the message sent through IPC */
|
||||
typedef struct {
|
||||
int command;
|
||||
uint32_t value;
|
||||
} ipc_msg_t;
|
||||
|
||||
#endif /* COMMON_IPC_H */
|
||||
@@ -1,7 +0,0 @@
|
||||
CompileFlags:
|
||||
Add:
|
||||
# Architecture and cross-compilation
|
||||
- "--target=aarch64-linux-gnu"
|
||||
|
||||
# Setup sysroot for buildroot
|
||||
- "--sysroot=/buildroot/output/host/aarch64-buildroot-linux-gnu/sysroot"
|
||||
@@ -1,7 +0,0 @@
|
||||
EXE=daemon
|
||||
SRCS=$(shell find . -name "*.c")
|
||||
LDFLAGS+=-lpthread
|
||||
EXTRA_CFLAGS+=-I.
|
||||
|
||||
# Include the standard application Makefile for the CSEL1 labs
|
||||
include ../../appl.mk
|
||||
@@ -1,149 +0,0 @@
|
||||
#include "app.h"
|
||||
#include "sysfs.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
static LED* led;
|
||||
|
||||
/* Threads objects */
|
||||
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 atomic_bool keep_running = true;
|
||||
|
||||
void btn_set_led(LED* l) {
|
||||
if (l != NULL) {
|
||||
led = l;
|
||||
}
|
||||
}
|
||||
|
||||
void set_period(uint32_t period_ms){
|
||||
sysfs_set_period(period_ms);
|
||||
}
|
||||
|
||||
uint32_t get_period() {
|
||||
return sysfs_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();
|
||||
if (current_period > GAP_PERIOD_MS) {
|
||||
set_period(current_period - GAP_PERIOD_MS);
|
||||
}
|
||||
pthread_mutex_unlock(&app_lock);
|
||||
}
|
||||
|
||||
int get_mode() {
|
||||
return sysfs_get_mode();
|
||||
}
|
||||
|
||||
void set_mode(int mode) {
|
||||
sysfs_set_mode(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() {
|
||||
return sysfs_get_temperature();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* animation_worker() - The single thread that processes the queue
|
||||
*/
|
||||
static void* animation_worker(void* arg) {
|
||||
while (keep_running) {
|
||||
// ENTER CRITICAL SECTION
|
||||
pthread_mutex_lock(&anim_lock);
|
||||
|
||||
/*
|
||||
* As long as there are no animations to do, the thread sleeps here.
|
||||
* It consumes 0% CPU while waiting.
|
||||
*/
|
||||
while (pending_animations == 0 && keep_running) {
|
||||
/*
|
||||
* Wait signal to make animation.
|
||||
* The function unlocks the mutex to allow another process to add an animation.
|
||||
* The function forces the thread to sleep until a signal is received.
|
||||
*/
|
||||
pthread_cond_wait(&anim_condition, &anim_lock);
|
||||
}
|
||||
|
||||
if (!keep_running) {
|
||||
pthread_mutex_unlock(&anim_lock);
|
||||
break;
|
||||
}
|
||||
|
||||
/* We take one animation from the queue */
|
||||
pending_animations--;
|
||||
|
||||
pthread_mutex_unlock(&anim_lock);
|
||||
// LEAVE CRITICAL SECTION
|
||||
|
||||
/* Perform the visual task */
|
||||
if (led != NULL) {
|
||||
LED_on(led);
|
||||
usleep(20000);
|
||||
LED_off(led);
|
||||
usleep(50000); /* Small delay between consecutive pulses */
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* init_animations() - To be called once during daemon startup
|
||||
*/
|
||||
void init_animations(void) {
|
||||
pthread_create(&anim_thread_id, NULL, animation_worker, NULL);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* trigger_pulse() - Increments the counter and wakes up the thread
|
||||
*/
|
||||
static void trigger_pulse(void) {
|
||||
|
||||
// ENTER CRITICAL SECTION
|
||||
pthread_mutex_lock(&anim_lock);
|
||||
|
||||
pending_animations++;
|
||||
|
||||
/* Signal the thread that there is work to do */
|
||||
pthread_cond_signal(&anim_condition);
|
||||
|
||||
pthread_mutex_unlock(&anim_lock);
|
||||
// LEAVE CRITICAL SECTION
|
||||
}
|
||||
|
||||
void btn_increase_period() {
|
||||
trigger_pulse();
|
||||
increase_period();
|
||||
}
|
||||
|
||||
void btn_decrease_period() {
|
||||
trigger_pulse();
|
||||
decrease_period();
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#ifndef APP_H
|
||||
#define APP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../gpio/led.h"
|
||||
|
||||
#define DEFAULT_PERIOD_MS 1000
|
||||
|
||||
#define GAP_PERIOD_MS 50
|
||||
|
||||
/* --- Period functions --- */
|
||||
void increase_period();
|
||||
void decrease_period();
|
||||
void set_period(uint32_t period_ms);
|
||||
uint32_t get_period();
|
||||
|
||||
|
||||
/* --- Mode functions --- */
|
||||
void mode_toggle();
|
||||
void set_mode(int mode);
|
||||
int get_mode();
|
||||
|
||||
|
||||
/* --- Temperature functions --- */
|
||||
float get_temperature();
|
||||
|
||||
|
||||
/* --- Button specific function --- */
|
||||
|
||||
void btn_set_led(LED* l);
|
||||
void init_animations(void);
|
||||
|
||||
void btn_increase_period();
|
||||
void btn_decrease_period();
|
||||
|
||||
#endif /* APP_H */
|
||||
@@ -1,91 +0,0 @@
|
||||
#include "sysfs.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.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 */
|
||||
}
|
||||
|
||||
float temp = 0.0f;
|
||||
|
||||
/*
|
||||
* The kernel formats the temperature as "XX.YYY" (e.g., "41.730").
|
||||
* Since we are in user space, we can simply read it as a float.
|
||||
*/
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
pthread_mutex_unlock(&sysfs_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(f, "%u\n", period);
|
||||
fclose(f);
|
||||
pthread_mutex_unlock(&sysfs_lock);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef SYSFS_H
|
||||
#define SYSFS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Define the absolute paths to the sysfs files created by the kernel */
|
||||
#define SYSFS_BASE_PATH "/sys/class/temp_regulator/regulator"
|
||||
#define PATH_TEMPERATURE SYSFS_BASE_PATH "/temperature"
|
||||
#define PATH_MODE SYSFS_BASE_PATH "/mode"
|
||||
#define PATH_PERIOD_ST SYSFS_BASE_PATH "/period_status"
|
||||
#define PATH_PERIOD_SET SYSFS_BASE_PATH "/period_set"
|
||||
|
||||
float sysfs_get_temperature();
|
||||
uint32_t sysfs_get_mode();
|
||||
void sysfs_set_mode(uint32_t mode);
|
||||
uint32_t sysfs_get_period();
|
||||
void sysfs_set_period(uint32_t period);
|
||||
|
||||
|
||||
#endif /* SYSFS_H */
|
||||
@@ -1,190 +0,0 @@
|
||||
#include "button.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define GPIO_EXPORT "/sys/class/gpio/export"
|
||||
#define GPIO_UNEXPORT "/sys/class/gpio/unexport"
|
||||
#define GPIO_BTN_BASE "/sys/class/gpio/gpio"
|
||||
|
||||
#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;
|
||||
pthread_t epoll_thread_id;
|
||||
|
||||
void BTN_add_epoll_event(BTN* btn, int tail);
|
||||
void epoll_init();
|
||||
static void* epoll_thread(void* arg);
|
||||
|
||||
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) {
|
||||
case BTN_INCREASE:
|
||||
strcpy(btn->pin, GPIO_BTN_INCREASE);
|
||||
break;
|
||||
case BTN_DECREASE:
|
||||
strcpy(btn->pin, GPIO_BTN_DECREASE);
|
||||
break;
|
||||
case BTN_MODE:
|
||||
strcpy(btn->pin, GPIO_BTN_MODE);
|
||||
break;
|
||||
default:
|
||||
printf("Invalid button type\n");
|
||||
return -1;
|
||||
}
|
||||
strcat(gpio_path, btn->pin);
|
||||
|
||||
int f = open(GPIO_UNEXPORT, O_WRONLY);
|
||||
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);
|
||||
|
||||
char direction_path[100];
|
||||
strcpy(direction_path, gpio_path);
|
||||
strcat(direction_path, "/direction");
|
||||
|
||||
f = open(direction_path, O_WRONLY);
|
||||
if (f == -1) return -1;
|
||||
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);
|
||||
if (f == -1) return -1;
|
||||
write(f, "both", 4);
|
||||
close(f);
|
||||
|
||||
char value_path[100];
|
||||
strcpy(value_path, gpio_path);
|
||||
strcat(value_path, "/value");
|
||||
|
||||
f = open(value_path, O_RDONLY);
|
||||
if (f == -1) {
|
||||
printf("Failed to setup button on pin %s\n", btn->pin);
|
||||
return -1;
|
||||
}
|
||||
btn->fd = f;
|
||||
|
||||
// Dummy read to clear initial state before waiting
|
||||
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");
|
||||
close(btn->fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
btn_list[tail] = btn;
|
||||
if (tail == 0) {
|
||||
epoll_init();
|
||||
}
|
||||
BTN_add_epoll_event(btn, tail);
|
||||
pthread_mutex_unlock(&btn_list_mutex);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.ptr = btn;
|
||||
|
||||
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, btn->fd, &ev[tail]);
|
||||
if (ret < 0) {
|
||||
perror("Failed to add epoll event");
|
||||
}
|
||||
}
|
||||
|
||||
void epoll_init(){
|
||||
int epfd = epoll_create1(0);
|
||||
if (epfd < 0) {
|
||||
perror("Failed to create epoll instance");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
epoll_fd = epfd;
|
||||
|
||||
if (pthread_create(&epoll_thread_id, NULL, epoll_thread, NULL) != 0) {
|
||||
perror("Failed to create timer thread");
|
||||
exit(30);
|
||||
}
|
||||
}
|
||||
|
||||
static void* epoll_thread(void* arg) {
|
||||
(void) arg;
|
||||
while (1) {
|
||||
struct epoll_event events[MAX_BTN];
|
||||
int n = epoll_wait(epoll_fd, events, MAX_BTN, -1);
|
||||
if (n < 0) {
|
||||
perror("epoll_wait");
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
char buf[2];
|
||||
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,29 +0,0 @@
|
||||
#ifndef BUTTON_H
|
||||
#define BUTTON_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#define GPIO_BTN_INCREASE "3"
|
||||
#define GPIO_BTN_DECREASE "0"
|
||||
#define GPIO_BTN_MODE "2"
|
||||
|
||||
typedef enum {
|
||||
BTN_INCREASE,
|
||||
BTN_DECREASE,
|
||||
BTN_MODE,
|
||||
} BTN_type;
|
||||
|
||||
typedef void (*BTN_callback)();
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
char pin[32];
|
||||
BTN_callback callback;
|
||||
pthread_mutex_t mutex;
|
||||
} BTN;
|
||||
|
||||
int BTN_init(BTN* btn, BTN_type type);
|
||||
void BTN_deinit(BTN* btn);
|
||||
void BTN_set_callback(BTN* btn, BTN_callback callback);
|
||||
|
||||
#endif
|
||||
@@ -1,115 +0,0 @@
|
||||
#include "led.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define GPIO_EXPORT "/sys/class/gpio/export"
|
||||
#define GPIO_UNEXPORT "/sys/class/gpio/unexport"
|
||||
#define GPIO_LED_BASE "/sys/class/gpio/gpio"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
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;
|
||||
char pin[32];
|
||||
switch (type) {
|
||||
case LED_STATUS:
|
||||
strcpy(pin, GPIO_LED_STATUS);
|
||||
break;
|
||||
case LED_POWER:
|
||||
strcpy(pin, GPIO_LED_POWER);
|
||||
break;
|
||||
default:
|
||||
printf("Invalid LED type\n");
|
||||
return -1;
|
||||
}
|
||||
strcat(gpio_path, 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);
|
||||
if (f == -1) {
|
||||
printf("Failed to setup led on pin %s\n", pin);
|
||||
return -1;
|
||||
}
|
||||
|
||||
led->gpio = f;
|
||||
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') {
|
||||
pwrite(led->gpio, "1", sizeof("1"), 0);
|
||||
} else {
|
||||
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,25 +0,0 @@
|
||||
#ifndef LED_H
|
||||
#define LED_H
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#define GPIO_LED_STATUS "10"
|
||||
#define GPIO_LED_POWER "362"
|
||||
|
||||
typedef enum {
|
||||
LED_STATUS, // gpioa.10 --> gpio10
|
||||
LED_POWER, // gpiol.10 --> gpio362
|
||||
} LED_type;
|
||||
|
||||
typedef struct {
|
||||
int gpio;
|
||||
pthread_mutex_t mutex;
|
||||
} LED;
|
||||
|
||||
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
|
||||
@@ -1,171 +0,0 @@
|
||||
// ipc_socket.c
|
||||
#include "ipc_server.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#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 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) {
|
||||
current_cbs.on_set_mode((int) msg->value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_SET_PERIOD:
|
||||
if (current_cbs.on_set_period) {
|
||||
current_cbs.on_set_period((uint32_t) msg->value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_INC_PERIOD:
|
||||
if (current_cbs.on_inc_period) {
|
||||
current_cbs.on_inc_period();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_DEC_PERIOD:
|
||||
if (current_cbs.on_dec_period) {
|
||||
current_cbs.on_dec_period();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("[IPC] Received unknown command: %d\n", msg->command);
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(&cb_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* ipc_thread_func() - The main loop of the background IPC thread.
|
||||
*/
|
||||
static void *ipc_thread_func(void *arg) {
|
||||
ipc_msg_t msg;
|
||||
ssize_t bytes_read;
|
||||
|
||||
printf("[IPC] Thread started. Listening on %s\n", SOCKET_PATH);
|
||||
|
||||
while (is_running) {
|
||||
/* Block until a message arrives or the socket is closed */
|
||||
bytes_read = recvfrom(server_fd, &msg, sizeof(msg), 0, NULL, NULL);
|
||||
|
||||
if (bytes_read == sizeof(ipc_msg_t)) {
|
||||
process_message(&msg);
|
||||
}
|
||||
else if (bytes_read <= 0 && is_running == 0) {
|
||||
/* Normal exit condition when shutdown() is called */
|
||||
break;
|
||||
}
|
||||
else if (bytes_read > 0) {
|
||||
printf("[IPC] Received malformed message (size %zd)\n", bytes_read);
|
||||
}
|
||||
else {
|
||||
perror("[IPC] Error receiving message");
|
||||
}
|
||||
}
|
||||
|
||||
printf("[IPC] Thread stopped.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* --- Public functions --- */
|
||||
|
||||
int start_ipc_server(struct ipc_callbacks_t *cbs) {
|
||||
struct sockaddr_un server_addr;
|
||||
|
||||
if (is_running) {
|
||||
printf("[IPC] Server is already running.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
if (server_fd < 0) {
|
||||
perror("[IPC] Failed to create socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Bind socket to the file path */
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sun_family = AF_UNIX;
|
||||
strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
|
||||
|
||||
unlink(SOCKET_PATH); /* Clean up existing orphaned socket file */
|
||||
|
||||
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
|
||||
perror("[IPC] Failed to bind socket");
|
||||
close(server_fd);
|
||||
server_fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Start the background thread */
|
||||
is_running = 1;
|
||||
if (pthread_create(&ipc_thread, NULL, ipc_thread_func, NULL) != 0) {
|
||||
perror("[IPC] Failed to create IPC thread");
|
||||
is_running = 0;
|
||||
close(server_fd);
|
||||
unlink(SOCKET_PATH);
|
||||
server_fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stop_ipc_server(void) {
|
||||
if (!is_running) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("[IPC] Stopping server...\n");
|
||||
|
||||
/* Signal thread to stop */
|
||||
atomic_store(&is_running, false);
|
||||
|
||||
/* Unblock the recvfrom() by shutting down the socket */
|
||||
if (server_fd != -1) {
|
||||
shutdown(server_fd, SHUT_RDWR);
|
||||
close(server_fd);
|
||||
server_fd = -1;
|
||||
}
|
||||
|
||||
/* Wait for thread to exit */
|
||||
pthread_join(ipc_thread, NULL);
|
||||
|
||||
/* Remove the socket file */
|
||||
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;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#ifndef IPC_SERVER_H
|
||||
#define IPC_SERVER_H
|
||||
|
||||
#include "../../common/common_ipc.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* Structure holding the callbacks for IPC commands.
|
||||
* Each function pointer will be called when the corresponding command is received.
|
||||
*/
|
||||
struct ipc_callbacks_t {
|
||||
void (*on_set_mode)(int mode);
|
||||
void (*on_set_period)(uint32_t period_ms);
|
||||
void (*on_inc_period)(void);
|
||||
void (*on_dec_period)(void);
|
||||
} ;
|
||||
|
||||
/**
|
||||
* start_ipc_server() - Start the IPC background thread.
|
||||
* @cbs: Pointer to the structure containing the callback functions.
|
||||
*
|
||||
* Return: 0 on success, or a negative value on error.
|
||||
*/
|
||||
int start_ipc_server(struct ipc_callbacks_t *cbs);
|
||||
|
||||
/**
|
||||
* stop_ipc_server() - Stop the IPC thread and clean up the socket.
|
||||
*
|
||||
* Return: 0 on success.
|
||||
*/
|
||||
int stop_ipc_server(void);
|
||||
|
||||
#endif //IPC_SERVER_H
|
||||
@@ -1,14 +0,0 @@
|
||||
@default:
|
||||
just --list
|
||||
|
||||
@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
|
||||
@@ -1,83 +0,0 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <pthread.h>
|
||||
#include <syslog.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#include "gpio/led.h"
|
||||
#include "gpio/button.h"
|
||||
#include "ipc/ipc_server.h"
|
||||
#include "oled/oled.h"
|
||||
#include "application/app.h"
|
||||
|
||||
#define DEFAULT_TIME_MS 1000
|
||||
#define DUTY_CYCLE_PERCENT 2
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
LED led_power;
|
||||
if (LED_init(&led_power, LED_POWER) != 0) {
|
||||
fprintf(stderr, "Failed to initialize power LED\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
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,
|
||||
.on_inc_period = increase_period,
|
||||
.on_set_period = set_period,
|
||||
.on_set_mode = set_mode,
|
||||
};
|
||||
|
||||
struct oled_callbacks_t oled_cbs = {
|
||||
.get_mode = get_mode,
|
||||
.get_period = get_period,
|
||||
.get_temperature = get_temperature,
|
||||
};
|
||||
|
||||
int ret = start_ipc_server(&ipc_cbs);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Failed to start IPC server: %d\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
init_oled(&oled_cbs);
|
||||
|
||||
btn_set_led(&led_power);
|
||||
init_animations();
|
||||
|
||||
while (1) {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
stop_ipc_server();
|
||||
LED_deinit(&led_power);
|
||||
BTN_deinit(&btn_inc);
|
||||
BTN_deinit(&btn_dec);
|
||||
BTN_deinit(&btn_mode);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
#include "oled.h"
|
||||
|
||||
#include "ssd1306.h"
|
||||
|
||||
#include "oled.h"
|
||||
#include "ssd1306.h"
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Internal reference to the registered callbacks */
|
||||
static struct oled_callbacks_t* oled_cbs = NULL;
|
||||
|
||||
/* Thread handle for the background update loop */
|
||||
static pthread_t oled_thread;
|
||||
|
||||
/**
|
||||
* display() - Formats and sends dynamic data to the OLED screen.
|
||||
*
|
||||
* This function uses snprintf to convert numerical values into strings
|
||||
* before sending them to the SSD1306 controller.
|
||||
*/
|
||||
static void display(int mode, float temp, uint32_t period_ms) {
|
||||
char buffer[20];
|
||||
|
||||
/* Static header rows */
|
||||
ssd1306_set_position(0, 0);
|
||||
ssd1306_puts("CSEL1a - SP.07");
|
||||
ssd1306_set_position(0, 1);
|
||||
ssd1306_puts(" WATCHDOG CPU ");
|
||||
ssd1306_set_position(0, 2);
|
||||
ssd1306_puts("--------------");
|
||||
|
||||
/* Mode display: switches between AUTO and MANU strings */
|
||||
ssd1306_set_position(0, 3);
|
||||
snprintf(buffer, sizeof(buffer), "Mode: %s", mode ? "AUTO " : "MANUAL");
|
||||
ssd1306_puts(buffer);
|
||||
|
||||
/* Temperature display: formatted with one decimal precision */
|
||||
ssd1306_set_position(0, 4);
|
||||
snprintf(buffer, sizeof(buffer), "Temp: %.1f'C", temp);
|
||||
ssd1306_puts(buffer);
|
||||
|
||||
/* Period/Frequency display: shows the value in milliseconds */
|
||||
ssd1306_set_position(0, 5);
|
||||
float freq = 1.0f / (period_ms / 1000.0f);
|
||||
snprintf(buffer, sizeof(buffer), "Freq: %.2fHz ", freq);
|
||||
ssd1306_puts(buffer);
|
||||
|
||||
ssd1306_set_position(0, 6);
|
||||
snprintf(buffer, sizeof(buffer), "Period: %dms ", period_ms);
|
||||
ssd1306_puts(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* update_oled_thread() - Continuous loop for screen refreshing.
|
||||
*
|
||||
* Runs at a 250ms interval to provide a responsive display without
|
||||
* overloading the I2C bus.
|
||||
*/
|
||||
static void *update_oled_thread(void* arg) {
|
||||
while (1) {
|
||||
if (oled_cbs != NULL) {
|
||||
/* Fetch fresh data using callbacks and update screen */
|
||||
display(
|
||||
oled_cbs->get_mode(),
|
||||
oled_cbs->get_temperature(),
|
||||
oled_cbs->get_period()
|
||||
);
|
||||
}
|
||||
/* Sleep to control refresh rate and save CPU cycles */
|
||||
usleep(250000);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void init_oled(struct oled_callbacks_t* cbs) {
|
||||
if (cbs != NULL) {
|
||||
oled_cbs = cbs;
|
||||
}
|
||||
|
||||
/* Initialize the hardware driver */
|
||||
ssd1306_init();
|
||||
|
||||
/* Start the background thread for automatic updates */
|
||||
pthread_create(&oled_thread, NULL, update_oled_thread, NULL);
|
||||
pthread_detach(oled_thread);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef MSE_MA_CSEL_OLED_H
|
||||
#define MSE_MA_CSEL_OLED_H
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Callback structure to decouple the OLED module from the application logic */
|
||||
struct oled_callbacks_t {
|
||||
float (*get_temperature)(void);
|
||||
int (*get_mode)(void);
|
||||
uint32_t (*get_period)(void);
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize the SSD1306 display and start the background refresh thread.
|
||||
* The cbs parameter provides the functions needed to fetch real-time data.
|
||||
*/
|
||||
void init_oled(struct oled_callbacks_t* cbs);
|
||||
|
||||
#endif //MSE_MA_CSEL_OLED_H
|
||||
@@ -1,233 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ssd1306.h"
|
||||
|
||||
// this code is mainly based on Bakebit software: https://github.com/friendlyarm/BakeBit
|
||||
|
||||
#define ARRAY_OF(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
#define I2C_BUS "/dev/i2c-0"
|
||||
#define OLED_ADDR 0x3c
|
||||
#define OLED_COMMAND_MODE 0x00
|
||||
#define OLED_DATA_MODE 0x40
|
||||
#define OLED_DISPLAY_OFF_CMD 0xae
|
||||
#define OLED_DISPLAY_ON_CMD 0xaf
|
||||
|
||||
static const uint8_t font[][8] = {
|
||||
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x00,0x5F,0x00,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x00,0x07,0x00,0x07,0x00,0x00,0x00},
|
||||
{0x00,0x14,0x7F,0x14,0x7F,0x14,0x00,0x00},
|
||||
{0x00,0x24,0x2A,0x7F,0x2A,0x12,0x00,0x00},
|
||||
{0x00,0x23,0x13,0x08,0x64,0x62,0x00,0x00},
|
||||
{0x00,0x36,0x49,0x55,0x22,0x50,0x00,0x00},
|
||||
{0x00,0x00,0x05,0x03,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x1C,0x22,0x41,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x41,0x22,0x1C,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x08,0x2A,0x1C,0x2A,0x08,0x00,0x00},
|
||||
{0x00,0x08,0x08,0x3E,0x08,0x08,0x00,0x00},
|
||||
{0x00,0xA0,0x60,0x00,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x00},
|
||||
{0x00,0x60,0x60,0x00,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x20,0x10,0x08,0x04,0x02,0x00,0x00},
|
||||
{0x00,0x3E,0x51,0x49,0x45,0x3E,0x00,0x00},
|
||||
{0x00,0x00,0x42,0x7F,0x40,0x00,0x00,0x00},
|
||||
{0x00,0x62,0x51,0x49,0x49,0x46,0x00,0x00},
|
||||
{0x00,0x22,0x41,0x49,0x49,0x36,0x00,0x00},
|
||||
{0x00,0x18,0x14,0x12,0x7F,0x10,0x00,0x00},
|
||||
{0x00,0x27,0x45,0x45,0x45,0x39,0x00,0x00},
|
||||
{0x00,0x3C,0x4A,0x49,0x49,0x30,0x00,0x00},
|
||||
{0x00,0x01,0x71,0x09,0x05,0x03,0x00,0x00},
|
||||
{0x00,0x36,0x49,0x49,0x49,0x36,0x00,0x00},
|
||||
{0x00,0x06,0x49,0x49,0x29,0x1E,0x00,0x00},
|
||||
{0x00,0x00,0x36,0x36,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x00,0xAC,0x6C,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x00},
|
||||
{0x00,0x14,0x14,0x14,0x14,0x14,0x00,0x00},
|
||||
{0x00,0x41,0x22,0x14,0x08,0x00,0x00,0x00},
|
||||
{0x00,0x02,0x01,0x51,0x09,0x06,0x00,0x00},
|
||||
{0x00,0x32,0x49,0x79,0x41,0x3E,0x00,0x00},
|
||||
{0x00,0x7E,0x09,0x09,0x09,0x7E,0x00,0x00},
|
||||
{0x00,0x7F,0x49,0x49,0x49,0x36,0x00,0x00},
|
||||
{0x00,0x3E,0x41,0x41,0x41,0x22,0x00,0x00},
|
||||
{0x00,0x7F,0x41,0x41,0x22,0x1C,0x00,0x00},
|
||||
{0x00,0x7F,0x49,0x49,0x49,0x41,0x00,0x00},
|
||||
{0x00,0x7F,0x09,0x09,0x09,0x01,0x00,0x00},
|
||||
{0x00,0x3E,0x41,0x41,0x51,0x72,0x00,0x00},
|
||||
{0x00,0x7F,0x08,0x08,0x08,0x7F,0x00,0x00},
|
||||
{0x00,0x41,0x7F,0x41,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x20,0x40,0x41,0x3F,0x01,0x00,0x00},
|
||||
{0x00,0x7F,0x08,0x14,0x22,0x41,0x00,0x00},
|
||||
{0x00,0x7F,0x40,0x40,0x40,0x40,0x00,0x00},
|
||||
{0x00,0x7F,0x02,0x0C,0x02,0x7F,0x00,0x00},
|
||||
{0x00,0x7F,0x04,0x08,0x10,0x7F,0x00,0x00},
|
||||
{0x00,0x3E,0x41,0x41,0x41,0x3E,0x00,0x00},
|
||||
{0x00,0x7F,0x09,0x09,0x09,0x06,0x00,0x00},
|
||||
{0x00,0x3E,0x41,0x51,0x21,0x5E,0x00,0x00},
|
||||
{0x00,0x7F,0x09,0x19,0x29,0x46,0x00,0x00},
|
||||
{0x00,0x26,0x49,0x49,0x49,0x32,0x00,0x00},
|
||||
{0x00,0x01,0x01,0x7F,0x01,0x01,0x00,0x00},
|
||||
{0x00,0x3F,0x40,0x40,0x40,0x3F,0x00,0x00},
|
||||
{0x00,0x1F,0x20,0x40,0x20,0x1F,0x00,0x00},
|
||||
{0x00,0x3F,0x40,0x38,0x40,0x3F,0x00,0x00},
|
||||
{0x00,0x63,0x14,0x08,0x14,0x63,0x00,0x00},
|
||||
{0x00,0x03,0x04,0x78,0x04,0x03,0x00,0x00},
|
||||
{0x00,0x61,0x51,0x49,0x45,0x43,0x00,0x00},
|
||||
{0x00,0x7F,0x41,0x41,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x02,0x04,0x08,0x10,0x20,0x00,0x00},
|
||||
{0x00,0x41,0x41,0x7F,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x04,0x02,0x01,0x02,0x04,0x00,0x00},
|
||||
{0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00},
|
||||
{0x00,0x01,0x02,0x04,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x20,0x54,0x54,0x54,0x78,0x00,0x00},
|
||||
{0x00,0x7F,0x48,0x44,0x44,0x38,0x00,0x00},
|
||||
{0x00,0x38,0x44,0x44,0x28,0x00,0x00,0x00},
|
||||
{0x00,0x38,0x44,0x44,0x48,0x7F,0x00,0x00},
|
||||
{0x00,0x38,0x54,0x54,0x54,0x18,0x00,0x00},
|
||||
{0x00,0x08,0x7E,0x09,0x02,0x00,0x00,0x00},
|
||||
{0x00,0x18,0xA4,0xA4,0xA4,0x7C,0x00,0x00},
|
||||
{0x00,0x7F,0x08,0x04,0x04,0x78,0x00,0x00},
|
||||
{0x00,0x00,0x7D,0x00,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x80,0x84,0x7D,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x7F,0x10,0x28,0x44,0x00,0x00,0x00},
|
||||
{0x00,0x41,0x7F,0x40,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x7C,0x04,0x18,0x04,0x78,0x00,0x00},
|
||||
{0x00,0x7C,0x08,0x04,0x7C,0x00,0x00,0x00},
|
||||
{0x00,0x38,0x44,0x44,0x38,0x00,0x00,0x00},
|
||||
{0x00,0xFC,0x24,0x24,0x18,0x00,0x00,0x00},
|
||||
{0x00,0x18,0x24,0x24,0xFC,0x00,0x00,0x00},
|
||||
{0x00,0x00,0x7C,0x08,0x04,0x00,0x00,0x00},
|
||||
{0x00,0x48,0x54,0x54,0x24,0x00,0x00,0x00},
|
||||
{0x00,0x04,0x7F,0x44,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x3C,0x40,0x40,0x7C,0x00,0x00,0x00},
|
||||
{0x00,0x1C,0x20,0x40,0x20,0x1C,0x00,0x00},
|
||||
{0x00,0x3C,0x40,0x30,0x40,0x3C,0x00,0x00},
|
||||
{0x00,0x44,0x28,0x10,0x28,0x44,0x00,0x00},
|
||||
{0x00,0x1C,0xA0,0xA0,0x7C,0x00,0x00,0x00},
|
||||
{0x00,0x44,0x64,0x54,0x4C,0x44,0x00,0x00},
|
||||
{0x00,0x08,0x36,0x41,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x41,0x36,0x08,0x00,0x00,0x00,0x00},
|
||||
{0x00,0x02,0x01,0x01,0x02,0x01,0x00,0x00},
|
||||
{0x00,0x02,0x05,0x05,0x02,0x00,0x00,0x00},
|
||||
};
|
||||
|
||||
|
||||
static const uint8_t init_commands[] = {
|
||||
0xAE, //display off
|
||||
0x00, //set lower column address
|
||||
0x10, //set higher column address
|
||||
0x40, //set display start line
|
||||
0xB0, //set page address
|
||||
0x81,
|
||||
0xCF, //0~255????????????????
|
||||
0xA1, //set segment remap
|
||||
0xA6, //normal / reverse
|
||||
0xA8, //multiplex ratio
|
||||
0x3F, //duty = 1/64
|
||||
0xC8, //Com scan direction
|
||||
0xD3, //set display offset
|
||||
0x00,
|
||||
0xD5, //set osc division
|
||||
0x80,
|
||||
0xD9, //set pre-charge period
|
||||
0xF1,
|
||||
0xDA, //set COM pins
|
||||
0x12,
|
||||
0xDB, //set vcomh
|
||||
0x40,
|
||||
0x8D, //set charge pump enable
|
||||
0x14,
|
||||
0xAF, //display ON
|
||||
|
||||
};
|
||||
|
||||
static int fd;
|
||||
|
||||
static void send_command(uint8_t cmd)
|
||||
{
|
||||
uint8_t buf[2]= {
|
||||
[0] = OLED_COMMAND_MODE,
|
||||
[1] = cmd,
|
||||
};
|
||||
if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
|
||||
printf ("error while sending command\n");
|
||||
}
|
||||
}
|
||||
|
||||
void send_data(uint8_t byte)
|
||||
{
|
||||
uint8_t buf[2] = {
|
||||
[0] = OLED_DATA_MODE,
|
||||
[1] = byte,
|
||||
};
|
||||
if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
|
||||
printf ("error while sending data\n");
|
||||
}
|
||||
}
|
||||
|
||||
void ssd1306_set_position (uint32_t column, uint32_t row)
|
||||
{
|
||||
send_command(0xb0 + row); //set page address
|
||||
send_command(0x00 + (8*column & 0x0f)); //set column lower address
|
||||
send_command(0x10 + ((8*column>>4)&0x0f)); //set column higher address
|
||||
}
|
||||
|
||||
void ssd1306_putc(char c)
|
||||
{
|
||||
if ((c<32) || (c>127)) // Ignore non-printable ASCII characters
|
||||
c=' ';
|
||||
c-=32;
|
||||
|
||||
for (int i=0; i<8; i++) {
|
||||
uint8_t data=font[(int)c][i];
|
||||
send_data(data);
|
||||
}
|
||||
}
|
||||
|
||||
void ssd1306_puts(const char* str)
|
||||
{
|
||||
while (*str != 0)
|
||||
ssd1306_putc(*str++);
|
||||
}
|
||||
|
||||
void ssd1306_clear_display()
|
||||
{
|
||||
send_command(OLED_DISPLAY_OFF_CMD);// display off
|
||||
for (int j=0; j<8; j++) {
|
||||
ssd1306_set_position(0,j);
|
||||
for (int i=0; i<16; i++) //clear all columns
|
||||
ssd1306_putc(' ');
|
||||
}
|
||||
send_command(OLED_DISPLAY_ON_CMD); //display on
|
||||
ssd1306_set_position(0,0);
|
||||
}
|
||||
|
||||
int ssd1306_init()
|
||||
{
|
||||
fd = open(I2C_BUS, O_RDWR);
|
||||
if (fd < 0) {
|
||||
printf("ERROR: unable to open i2c bus interface (%s)\n", I2C_BUS);
|
||||
return -1;
|
||||
}
|
||||
if (ioctl(fd, I2C_SLAVE, OLED_ADDR) < 0) {
|
||||
printf("ERROR: unable to access OLED as slave\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (unsigned i=0; i<ARRAY_OF(init_commands); i++) {
|
||||
send_command (init_commands[i]);
|
||||
}
|
||||
|
||||
ssd1306_clear_display();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef SSD1306_H
|
||||
#define SSD1306_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int ssd1306_init();
|
||||
void ssd1306_set_position (uint32_t column, uint32_t row);
|
||||
void ssd1306_putc(char c);
|
||||
void ssd1306_puts(const char* str);
|
||||
void ssd1306_clear_display();
|
||||
|
||||
#endif
|
||||
@@ -1,40 +0,0 @@
|
||||
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
|
||||
|
||||
# Clean, build, install, and run everything
|
||||
@all: clean build install run
|
||||
@@ -1,33 +0,0 @@
|
||||
CompileFlags:
|
||||
Add:
|
||||
# Architecture and cross-compilation
|
||||
- "--target=aarch64-linux-gnu"
|
||||
|
||||
# Exclude standard library
|
||||
- "-nostdinc"
|
||||
|
||||
# Mandatory kernel definitions
|
||||
- "-D__KERNEL__"
|
||||
- "-DMODULE"
|
||||
- "-DCONFIG_CC_HAS_K_CONSTRAINT=1"
|
||||
|
||||
# Force-included files
|
||||
- "-include"
|
||||
- "/buildroot/output/build/linux-5.15.148/include/linux/compiler-version.h"
|
||||
- "-include"
|
||||
- "/buildroot/output/build/linux-5.15.148/include/linux/kconfig.h"
|
||||
- "-include"
|
||||
- "/buildroot/output/build/linux-5.15.148/include/linux/compiler_types.h"
|
||||
|
||||
# Kernel include paths
|
||||
- "-I/buildroot/output/build/linux-5.15.148/arch/arm64/include"
|
||||
- "-I/buildroot/output/build/linux-5.15.148/arch/arm64/include/generated"
|
||||
- "-I/buildroot/output/build/linux-5.15.148/include"
|
||||
- "-I/buildroot/output/build/linux-5.15.148/arch/arm64/include/uapi"
|
||||
- "-I/buildroot/output/build/linux-5.15.148/arch/arm64/include/generated/uapi"
|
||||
- "-I/buildroot/output/build/linux-5.15.148/include/uapi"
|
||||
- "-I/buildroot/output/build/linux-5.15.148/include/generated/uapi"
|
||||
|
||||
# GCC compiler system include path
|
||||
- "-isystem"
|
||||
- "/buildroot/output/host/lib/gcc/aarch64-buildroot-linux-gnu/11.3.0/include"
|
||||
@@ -1,41 +0,0 @@
|
||||
export PATH := /buildroot/output/host/usr/sbin$\
|
||||
:/buildroot/output/host/usr/bin/$\
|
||||
:/buildroot/output/host/sbin$\
|
||||
:/buildroot/output/host/bin/$\
|
||||
:$(PATH)
|
||||
|
||||
MODULE := temp_regulator
|
||||
SOURCE := main
|
||||
|
||||
# Part executed when called from kernel build system:
|
||||
ifneq ($(KERNELRELEASE),)
|
||||
obj-m += $(MODULE).o ## name of the generated module
|
||||
|
||||
SUBDIR_SRCS := $(shell find $(src) -mindepth 2 -type f -name '*.c')
|
||||
SUBDIR_OBJS := $(patsubst $(src)/%.c,%.o,$(SUBDIR_SRCS))
|
||||
|
||||
$(MODULE)-objs := $(SOURCE).o $(SUBDIR_OBJS)
|
||||
|
||||
|
||||
# $(MODULE)-objs := $(SOURCE).o ## list of objects needed for that module
|
||||
CFLAGS_$(SOURCE).o := -DDEBUG
|
||||
|
||||
# Part executed when called from standard make in module source directory:
|
||||
else
|
||||
include ../../kernel_settings
|
||||
PWD := $(shell pwd)
|
||||
|
||||
all:
|
||||
$(MAKE) -C $(KDIR) M=$(PWD) ARCH=$(CPU) CROSS_COMPILE=$(TOOLS) modules
|
||||
|
||||
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
|
||||
|
||||
endif
|
||||
@@ -1,64 +0,0 @@
|
||||
#include "blink.h"
|
||||
|
||||
static struct task_struct* blink_thread;
|
||||
atomic_t blink_period_ms;
|
||||
|
||||
int thread_blinkThread(void* data);
|
||||
|
||||
void blink_init(void) {
|
||||
int ret = 0;
|
||||
pr_info("Initialize blink thread\n");
|
||||
|
||||
atomic_set(&blink_period_ms, DEFAULT_PERIOD_MS);
|
||||
|
||||
ret = gpio_request(GPIO_PIN, "blink-led");
|
||||
if(ret) {
|
||||
pr_err("Failed to request GPIO pin %d, error %d\n", GPIO_PIN, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = gpio_direction_output(GPIO_PIN, 0);
|
||||
if(ret) {
|
||||
pr_err("Failed to set GPIO pin %d direction, error %d\n", GPIO_PIN, ret);
|
||||
gpio_free(GPIO_PIN);
|
||||
return;
|
||||
}
|
||||
|
||||
blink_thread = kthread_run(thread_blinkThread, NULL, "blink_thread");
|
||||
if (IS_ERR(blink_thread)) {
|
||||
pr_err("Failed to create blink thread\n");
|
||||
gpio_free(GPIO_PIN);
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("Blink thread initialized\n");
|
||||
}
|
||||
|
||||
void blink_exit(void) {
|
||||
pr_info("Exiting blink thread\n");
|
||||
kthread_stop(blink_thread);
|
||||
gpio_free(GPIO_PIN);
|
||||
pr_info("Blink thread exited\n");
|
||||
}
|
||||
|
||||
int thread_blinkThread(void* data) {
|
||||
|
||||
bool state = false;
|
||||
int period = 0;
|
||||
|
||||
pr_info("Blink started\n");
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
gpio_set_value(GPIO_PIN, state);
|
||||
state = !state;
|
||||
period = atomic_read(&blink_period_ms);
|
||||
msleep(period >> 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void adjust_period(int new_period_ms) {
|
||||
pr_info("Adjusting blink period to %d ms\n", new_period_ms);
|
||||
atomic_set(&blink_period_ms, new_period_ms);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#ifndef BLINK_H
|
||||
#define BLINK_H
|
||||
|
||||
#include <linux/module.h> // needed by all modules
|
||||
#include <linux/init.h> // needed for macros
|
||||
#include <linux/kernel.h> // needed for debugging
|
||||
|
||||
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#include <linux/atomic.h>
|
||||
|
||||
|
||||
static const int GPIO_PIN = 10;
|
||||
static const int DEFAULT_PERIOD_MS = 1000;
|
||||
|
||||
/**
|
||||
* Allow to set a new period for the blinky led
|
||||
* @param new_period_ms The new period in ms for the blinky led
|
||||
*/
|
||||
void adjust_period(int new_period_ms);
|
||||
|
||||
void blink_init(void);
|
||||
void blink_exit(void);
|
||||
|
||||
#endif /* BLINK_H */
|
||||
@@ -1,11 +0,0 @@
|
||||
@default:
|
||||
just --list
|
||||
|
||||
@build:
|
||||
make
|
||||
|
||||
@install:
|
||||
make install
|
||||
|
||||
@clean:
|
||||
make clean
|
||||