1
0

56 Commits

Author SHA1 Message Date
9fbfac5fe1 feat(lab03): use timer on led on and off
+ separate timer function in dedicated class
2026-05-08 09:46:55 +02:00
22f923bfbe refactor(lab03): merge create and configure timer function 2026-05-08 09:46:55 +02:00
2d26f30adf feat(lab03): use epoll and timer for led 2026-05-08 09:46:55 +02:00
8561386973 feat(lab03): add epoll 2026-05-08 09:46:54 +02:00
5592c9c0fe fix(lab03): max value of nanosecond is 1s
Time is splited in second and nanosecond
2026-05-08 09:46:54 +02:00
ca8085ce09 feat(lab03): add timer + refactor base
- Split in several function
- Create timer
- Config timer

TODO: add epoll and connect the timer to this epoll
2026-05-08 09:46:54 +02:00
fb67d8aad7 fix(lab03): use right compiler on solution 2026-04-18 11:38:09 +00:00
5eddfb17a3 doc(lab03): init report 2026-04-18 11:37:07 +00:00
00bc81465c Revert "fix!(lab03): correct initialisation of the lab silly led"
Revert makefile in solution
2026-04-18 09:59:10 +00:00
95c86b3ea5 fix(lab03): use make instead of cmake 2026-04-18 09:27:37 +00:00
a9c9d11521 fix!(lab03): correct initialisation of the lab silly led
BREAKING CHANGE: we now use the scope according to src / report
2026-04-18 09:16:45 +00:00
a65c061642 chore(gitignore): add cmake files and folders 2026-04-17 16:11:36 +02:00
5aa46bc864 chore(lab04): add laboratory files 2026-04-17 16:11:03 +02:00
a8a1080180 doc(lab01): add png 2026-04-02 23:23:15 +02:00
5a9295edfb chore(lab03): typo in code 2026-04-02 23:21:20 +02:00
15783a04b0 chore(lab03): remove useless file 2026-04-02 23:20:57 +02:00
b72fca7b26 doc(lab03): add ex05 + typos 2026-04-02 23:20:35 +02:00
5978d25a1c doc(lab02): typo 2026-04-02 23:20:05 +02:00
6fb6bd811b doc(lab03): add to exercice 4 2026-04-02 22:56:28 +02:00
76699b8d19 doc: add end of report for lab01-module 2026-04-02 22:56:28 +02:00
6b0246b2a5 refactor: modify until exercice 4 2026-04-02 22:56:28 +02:00
aa7342123d refactor: change structur of report 2026-04-02 22:56:28 +02:00
8a610842f8 feat(lab03): add ex5 - sysfs 2026-04-02 22:54:39 +02:00
8cfadd1bf9 feat(lab03): add exercice 04 2026-04-02 22:03:14 +02:00
ff22715a6b fix(lab03): ex3 read and write fop 2026-04-02 22:02:48 +02:00
263b5c203e feat(lab03): WIP ex4 2026-04-02 21:16:33 +02:00
86a5f88481 fix(lab03): display right exercice number 2026-04-02 21:07:20 +02:00
c501d368d5 fix(lab03): orthograph 2026-04-02 20:14:39 +02:00
2130c96ada fix(lab03): finish ex 3 2026-04-02 20:05:31 +02:00
e3f234c088 chore(gitignore): ignore pdf in doc 2026-04-02 19:48:39 +02:00
03b6f0beda fix(doc): delete pdf 2026-04-02 19:46:40 +02:00
baf46b7929 fix(lab03): fops read and write data 2026-04-02 18:50:16 +02:00
f26133499c feat(lab03): add ex 3 2026-04-02 18:24:13 +02:00
83e10f098b doc(lab03): add doc for exercice 3 2026-04-02 16:28:38 +02:00
9725e2c66b feat(lab03): ex 2 in one file 2026-04-02 14:41:31 +02:00
beac78bf94 feat(lab03): add module for ex 3 2026-04-02 13:53:01 +02:00
62e059775c feat(lab03): update main with ex 1 2026-04-02 13:52:30 +02:00
183e6243eb feat(lab02): remake a folder for all exercices 2026-04-02 13:48:11 +02:00
1676b42d58 feat(lab03): refactor exrcice to make it in several files 2026-04-02 08:32:52 +02:00
a6d7b86637 feat(lab03): add ex1 with dev/mem 2026-04-01 21:50:39 +02:00
7f12a642e2 chore(driver): setup files and module 2026-04-01 20:55:21 +02:00
61cb96d2d7 chore(skeleton): rechange exercice files position 2026-04-01 20:54:07 +02:00
6ba497d3e2 chore(skeleton): refactor all serie exercices intoi subfolders 2026-04-01 20:07:17 +02:00
7a376ff789 doc(lab02): add doc for ex08 2026-04-01 18:42:18 +02:00
906954a035 feat(lab02): add code for ex08 2026-04-01 18:25:39 +02:00
59b7caf82e feat(lab02): add ex07 resouces 2026-04-01 13:07:56 +02:00
b1a1d6af60 doc(lab02): start doc for ex7 2026-04-01 13:07:06 +02:00
d477abe506 feat(lab02): add code for ex07 2026-04-01 10:20:35 +02:00
8fff875529 feat(lab02): add ex06 2026-03-31 21:06:44 +02:00
e4089d2e05 fix(lab02): avoid to free a null pointer 2026-03-31 21:06:05 +02:00
a6ef142912 doc(lab02): add doc for ex05 2026-03-31 16:35:21 +02:00
409e1565fb fix(lab02): typo for temperature calculation 2026-03-31 16:24:21 +02:00
7f46bd3940 refactor(lab02): split exercices for skeleton 2026-03-31 16:01:33 +02:00
786ed475ba fix(lab02): fix address in ex05 2026-03-31 14:11:35 +02:00
fb8de73be8 feat(lab02): WIP add ex05 2026-03-31 13:57:53 +02:00
da2639d6f3 feat(lab02): add ex04 2026-03-31 10:51:27 +02:00
53 changed files with 1772 additions and 351 deletions

4
.gitignore vendored
View File

@@ -54,3 +54,7 @@ solutions/**/build
boot-scripts/boot.cifs
boot-scripts/boot.net
doc/**/*.pdf
build
src/03-led-controller/led-controller

View File

@@ -1,14 +1,10 @@
// #import "@preview/hei-synd-report:0.1.1": *
#import "@preview/hei-synd-thesis:0.4.0": *
#import "/doc/metadata.typ": *
#import "/doc/resources/glossary.typ": *
#show:make-glossary
#register-glossary(entry-list)
#import "@preview/fractusist:0.1.1":*
#import "@preview/grape-suite:3.1.0": exercise
#import exercise: task, subtask
//-------------------------------------
// Template config
@@ -49,332 +45,31 @@
//-------------------------------------
// Content
//
= Embedded Linux Environment
In this laboratory, we see how to setup our environnement and how to have several way to boot. That include a `boot.cifs` that allow us to load the rootfs from samba to easily share the rootfs between the host and the target. And also a `boot.tftp` that allow us to load the kernel by tftp, which is really usefull when we want to modify the kernel and test it without having to reflash the whole system.
We also see how to debug our system with a remote debugger. That allow us to use debug in our code editor (vscode) a programm that run on the target.
#figure(
image("/doc/resources/img/dev-environment.drawio.svg"),
caption: "Development environment schema"
) <fig:dev-env>
== Questions
=== How to generate U-Boot?
We use buildroot, a tool to build embedded Linux.
It can generate the U-Boot bootloader.
With `make menuconfig`, we can select the U-Boot package.
U-boot can be configured with `make uboot-menuconfig`
And finally, when we have configured everything, we can build the whole system with `make` command.
Or only uboot with `make uboot` command.
=== How to add and build a additional package in Buildroot?
In buildroot, with `make menuconfig`, we can select the package we want in `Target packages` section. We can specifically build it with `make <package-name>` command. Otherwise, it will be built with the whole system when we run `make` command.
=== How to modify the Linux kernel configuration?
Like all package, with `make <package-name>-menuconfig` command. So, for Linux kernel:
```bash
|> make linux-menuconfig
```
=== How to generate a custom rootfs?
First of all, select the type of filesystem you want to generate in `Filesystem images` section of `make menuconfig`. We can use an overlay to customise our rootfs.
The overlay is a directory (inin the board folder) with the same structure as rootfs and it will merge with the generated rootfs. So, we can add files and directories in the overlay and they will be added to the final rootfs.
=== How to use the eMMC card instead of the SD card?
We need to change the boot script `(boot*.cmd)` to load from eMMC by changing the `fatload` command with the correct number. Probably 1 instead of 0.
```
fatload mmc 1 $kernel_addr_r Image
```
=== In cours support, we find several configurations of the development environment. What would be the optimal configuration for developing only user-space applications?
If we develop only user space program, we don't need to load kernel by tftp. But it's really usefull to have rootfs load by samba. So the best approach is to use the `boot.cifs`.
//--------------------------------------
#include "lab00-env/main.typ"
#pagebreak()
= Linux Kernel Programming
== Cheatsheet commands
- `modinfo <module.ko>`: display information about a kernel module
- `insmod <module.ko>`: install a kernel module (without checking for dependencies)
- `rmmod <module.ko>`: uninstall a kernel module
- `lsmod`: list the currently loaded kernel modules
- `dmesg`: display the kernel log
- `cat /proc/modules`: display the currently loaded kernel modules with more details
- `modprobe <module>`: install a kernel module and its dependencies
- `modprobe -r <module>`: uninstall a kernel module and its dependencies
- `make`: build the kernel module
- `make install`: install the kernel module in the root filesystem
== Exercises
//-------------------
// Exercise 1: Generate kernel module out of tree
//-------------------
#task(
[Generate kernel module out of tree],
[],
)
//--------------
#subtask[
Create the skeleton of a kernel module and generate it outside the kernel sources using a Makefile. The module should display a message when it is registered and when it is uninstalled.
]
We already have a skeleton in `src/02-modules/exercice01` (now `solutions/02_modules/exercice01` that we move to `src/01-skeleton`). We see on the Makefile that the module is generated outside the kernel sources with the `KDIR` variable imported from `src/kernel_settings`. This variable point to the kernel sources.
The Makefile also use the `PWD` variable to the current directory.
The `make` command will use these variables to generate the module in the current directory.
```makefile
$(MAKE) -C $(KDIR) M=$(PWD) ARCH=$(CPU) CROSS_COMPILE=$(TOOLS) modules
```
//--------------
#subtask[
Test on the host machine the command modinfo1 on your module skeleton and compare the information returned with that of the source code.
]
```bash
|> modinfo mymodule.ko
filename: /workspace/src/01-skeleton/mymodule.ko
license: GPL
description: Module skeleton
author: Klagarge <remi@heredero.ch>
author: Fastium <fastium.pro@proton.me>
depends:
name: mymodule
vermagic: 5.15.148 SMP preempt mod_unload aarch64
parm: text:charp
parm: elements:int
```
//--------------
#subtask[
Install the module (insmod) and check the kernel log (dmesg)
]
```bash
|> insmod mymodule.ko
[ 1727.896902] mymodule: loading out-of-tree module taints kernel.
[ 1727.903442] Linux module 01 skeleton loaded
```
We can see the module is indead out-of-tree and correctly loaded.
```bash
|> dmesg | tail -5
[ 1381.694764] CIFS: Attempting to mount \\192.168.53.4\workspace
[ 1727.896902] mymodule: loading out-of-tree module taints kernel.
[ 1727.903442] Linux module 01 skeleton loaded
[ 1727.907659] text: dummy text
[ 1727.907659] elements: 1
```
//--------------
#subtask[
Compare the results obtained by the lsmod command with those obtained with the cat /proc/modules command
]
```bash
|> lsmod
Module Size Used by Tainted: G
mymodule 16384 0
···
|> cat /proc/modules
mymodule 16384 0 - Live 0xffff8000011bf000 (O)
···
```
The `/proc/modules` file give us more details about the state of the module. We see it is now live (charged in memory and running)
//--------------
#subtask[
Uninstall the module (rmmod).
]
```bash
|> rmmod mymodule.ko
[ 2989.535793] Linux module skeleton unloaded
```
//--------------
#subtask[
Adapt the Makefile of the module to allow the installation of the module with other kernel modules allowing the use of the modprobe command. The module should be installed in the root filesystem used in cifs by the target.
]
```bash
# On host:
|> make install
# On target:
|> modprobe mymodule
[ 3359.811183] Linux module 01 skeleton loaded
```
#include "lab01-module/main.typ"
#pagebreak()
#include "lab02-peripheral/main.typ"
#pagebreak()
//-------------------
// Exercise 2: Adapt the kernel module to receive parameters
//-------------------
#task(
[Adapt the kernel module to receive parameters],
[
Adapt the kernel module of the previous exercise to receive two or three parameters of your choice. These parameters will be displayed in the console when the module is loaded.
],
)
```bash
|> modprobe mymodule
[ 3583.616662] Linux module skeleton ex02 loaded
|> dmesg | tail -5
[ 3559.279143] number: 1
[ 3581.198562] Linux module skeleton unloaded
[ 3583.616662] Linux module skeleton ex02 loaded
[ 3583.621085] text: The answer to the Ultimate Question of Life, The Universe, and Everything
[ 3583.621085] number: 42
|> modprobe -r mymodule
[ 3588.404778] Linux module skeleton unloaded
```
//-------------------
// Exercise 3: What does it mean the 4 values in ```/proc/sys/kernel/printk``` ?
//-------------------
#task(
[What does it mean the 4 values in ```/proc/sys/kernel/printk``` ?],
[]
)
We can show what there is in:
```bash
|> cat /proc/sys/kernel/printk
7 4 1 7
```
The number specified the level of output in a console.
This file specifies the log level for: \
current (7), default (4), minimum (1) and boot-time default (7).
This number matches with this table (#link("https://www.kernel.org/doc/html/latest/core-api/printk-basics.html", [printk documentation])):
#table(
columns: (2fr, 1fr, 3fr),
[*Name*], [*String*], [*Alias function*],
[KERN_EMERG], ["0"], [pr_emerg()],
[KERN_ALERT], ["1"], [pr_alert()],
[KERN_CRIT], ["2"], [pr_crit()],
[KERN_ERR], ["3"], [pr_err()],
[KERN_WARNING], ["4"], [pr_warning()],
[KERN_NOTICE], ["5"], [pr_notice()],
[KERN_INFO], ["6"], [pr_info()],
[KERN_DEBUG], ["7"], [pr_debug() and pr_devel() if DEBUG is defined],
[KERN_DEFAULT], [""], [],
[KERN_CONT], ["c"], [pr_cont()],
)
= #i18n("appendix-title", lang: option.lang) <sec:appendix>
== Exercices Lab 01
#include "lab01-module/ex01.typ"
#pagebreak()
//-------------------
// Exercise 4: Create module with dynamic allocation and a chained list
//-------------------
#task(
[
Create module with dynamic allocation and a chained list
],
[
Create dynamically elements in the kernel. Adapt a kernel module to specify at the installation the number of element to create a initial text.
Each element will contain a unique number. The elements are create at the installation of the module adn chained in a list.
These elements will be destruct during the uninstallation of the module.
Some information messages are emits to allow debugging.
]
)
//-------------------
// Exercise 5: Display the processor chip ID, CPU temperature and the MAC adress of the Ethernet controller
//-------------------
#task(
[
Display the processor chip ID, CPU temperature and the MAC adress of the Ethernet controller
],
[
- Chip ID registers: _0x01c1'4200_ to _0x01c1'420c_
- 32 bits register of the temperature sensor: _0x01c2'5080_
- two 32 bits registers of the Ethernet controller MAC address: _0x01c3'0050_ and _0x01c3'0054_
To calculate the temperature value, there is this formul:
$
"temperature" = -1991 dot "register value" / 10 + 223000
$
The chip ID can be verified in ```/proc/iomem```.
The register value of the temperature can be verified in the file: ```/sys/class/thermal/thermal_zone0/temp```.
The MAC address can be verified with ``` ifconfig```.
]
)
//-------------------
// Exercise 6: Kernel thread
//-------------------
#task(
[
Kernel thread
],
[
Develop a module which allows to instanciate a thread in the kernel. This thread will display a message every 5 seconds. Use the function ```ssleep(5)``` to sleep the thread from ``` linux/delay.h```.
]
)
//-------------------
// Exercise 7: Sleeping
//-------------------
#task(
[
Sleeping
],
[
Develop a module which instanciate 2 threads in the kernel. The first one will wait a wake up notification from the second thread and will sleep. The second will send the notification every 5 seconds. Then it will sleep. We will use the waitqueue for the sleeping function. To allow debugging, each thread will send a message when it wakes up.
]
)
//-------------------
// Exercise 8: Interrupts
//-------------------
#task(
[
Interrupts
],
[
Develop a module which allows to detect every push on the button of the nanopi with interrupt. Every interrupts will send a message for debugging.
- Use the service ``` gpio_request(<io_nr>, <label>)```
- Get the interrupt vector with ``` gpio_to_irq(<io_nr>)```
- Extension card information:
- k1 - gpio: A, pin_nr=0, io_nr=0
- k2 - gpio: A, pin_nr=2, io_nr=2
- k3 - gpio: A, pin_nr=3, io_nr=3
]
)
#include "lab01-module/ex02.typ"
#include "lab01-module/ex03.typ"
#pagebreak()
#include "lab01-module/ex04.typ"
#pagebreak()
#include "lab01-module/ex05.typ"
#include "lab01-module/ex06.typ"
#pagebreak()
#include "lab01-module/ex07.typ"
#include "lab01-module/ex08.typ"
//-------------------------------------
// Glossary
//
#heading(numbering:none, outlined: false)[] <sec:end>
#make_glossary(gloss:gloss, title:i18n("gloss-title"))
// #heading(numbering:none, outlined: false)[] <sec:end>
// #make_glossary(gloss:gloss, title:i18n("gloss-title"))

View File

@@ -47,7 +47,7 @@
// Content
//
= Linux System Programming
#include "lab03-silly_led/main.typ"
#lorem(150)

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

45
doc/lab00-env/main.typ Normal file
View File

@@ -0,0 +1,45 @@
= Embedded Linux Environment
In this laboratory, we see how to setup our environnement and how to have several way to boot. That include a `boot.cifs` that allow us to load the rootfs from samba to easily share the rootfs between the host and the target. And also a `boot.tftp` that allow us to load the kernel by tftp, which is really usefull when we want to modify the kernel and test it without having to reflash the whole system.
We also see how to debug our system with a remote debugger. That allow us to use debug in our code editor (vscode) a programm that run on the target.
#figure(
image("./dev-environment.png"),
caption: "Development environment schema"
) <fig:dev-env>
== Questions
=== How to generate U-Boot?
We use buildroot, a tool to build embedded Linux.
It can generate the U-Boot bootloader.
With `make menuconfig`, we can select the U-Boot package.
U-boot can be configured with `make uboot-menuconfig`
And finally, when we have configured everything, we can build the whole system with `make` command.
Or only uboot with `make uboot` command.
=== How to add and build a additional package in Buildroot?
In buildroot, with `make menuconfig`, we can select the package we want in `Target packages` section. We can specifically build it with `make <package-name>` command. Otherwise, it will be built with the whole system when we run `make` command.
=== How to modify the Linux kernel configuration?
Like all package, with `make <package-name>-menuconfig` command. So, for Linux kernel:
```bash
|> make linux-menuconfig
```
=== How to generate a custom rootfs?
First of all, select the type of filesystem you want to generate in `Filesystem images` section of `make menuconfig`. We can use an overlay to customise our rootfs.
The overlay is a directory (in the board folder) with the same structure as rootfs and it will merge with the generated rootfs. So, we can add files and directories in the overlay and they will be added to the final rootfs.
=== How to use the eMMC card instead of the SD card?
We need to change the boot script `(boot*.cmd)` to load from eMMC by changing the `fatload` command with the correct number. Probably 1 instead of 0.
```
fatload mmc 1 $kernel_addr_r Image
```
=== In cours support, we find several configurations of the development environment. What would be the optimal configuration for developing only user-space applications?
If we develop only user space program, we don't need to load kernel by tftp. But it's really usefull to have rootfs load by samba. So the best approach is to use the `boot.cifs`.

95
doc/lab01-module/ex01.typ Normal file
View File

@@ -0,0 +1,95 @@
#import "/doc/metadata.typ": *
=== Generate kernel module out of tree <lab01:ex01>
#colorbox(title: "Exercise", color: hei-blue)[
Create the skeleton of a kernel module and generate it outside the kernel sources using a Makefile. The module should display a message when it is registered and when it is uninstalled.
]
//--------------
We already have a skeleton in `src/02-modules/exercice01` (now `solutions/02_modules/exercice01` that we move to `src/01-skeleton`). We see on the Makefile that the module is generated outside the kernel sources with the `KDIR` variable imported from `src/kernel_settings`. This variable point to the kernel sources.
The Makefile also use the `PWD` variable to the current directory.
The `make` command will use these variables to generate the module in the current directory.
```makefile
$(MAKE) -C $(KDIR) M=$(PWD) ARCH=$(CPU) CROSS_COMPILE=$(TOOLS) modules
```
//--------------
#colorbox(title: "Exercise", color: hei-blue)[
Test on the host machine the command modinfo1 on your module skeleton and compare the information returned with that of the source code.
]
```bash
|> modinfo mymodule.ko
filename: /workspace/src/01-skeleton/mymodule.ko
license: GPL
description: Module skeleton
author: Klagarge <remi@heredero.ch>
author: Fastium <fastium.pro@proton.me>
depends:
name: mymodule
vermagic: 5.15.148 SMP preempt mod_unload aarch64
parm: text:charp
parm: elements:int
```
//--------------
#colorbox(title: "Exercise", color: hei-blue)[
Install the module (insmod) and check the kernel log (dmesg)
]
```bash
|> insmod mymodule.ko
[ 1727.896902] mymodule: loading out-of-tree module taints kernel.
[ 1727.903442] Linux module 01 skeleton loaded
```
We can see the module is indead out-of-tree and correctly loaded.
```bash
|> dmesg | tail -5
[ 1381.694764] CIFS: Attempting to mount \\192.168.53.4\workspace
[ 1727.896902] mymodule: loading out-of-tree module taints kernel.
[ 1727.903442] Linux module 01 skeleton loaded
[ 1727.907659] text: dummy text
[ 1727.907659] elements: 1
```
//--------------
#colorbox(title: "Exercise", color: hei-blue)[
Compare the results obtained by the lsmod command with those obtained with the cat /proc/modules command
]
```bash
|> lsmod
Module Size Used by Tainted: G
mymodule 16384 0
···
|> cat /proc/modules
mymodule 16384 0 - Live 0xffff8000011bf000 (O)
···
```
The `/proc/modules` file give us more details about the state of the module. We see it is now live (charged in memory and running)
//--------------
#colorbox(title: "Exercise", color: hei-blue)[
Uninstall the module (rmmod).
]
```bash
|> rmmod mymodule.ko
[ 2989.535793] Linux module skeleton unloaded
```
//--------------
#colorbox(title: "Exercise", color: hei-blue)[
Adapt the Makefile of the module to allow the installation of the module with other kernel modules allowing the use of the modprobe command. The module should be installed in the root filesystem used in cifs by the target.
]
```bash
# On host:
|> make install
# On target:
|> modprobe mymodule
[ 3359.811183] Linux module 01 skeleton loaded
```

19
doc/lab01-module/ex02.typ Normal file
View File

@@ -0,0 +1,19 @@
#import "/doc/metadata.typ": *
=== Adapt the kernel module to receive parameters <lab01:ex02>
#colorbox(title: "Exercise", color: hei-blue)[
Adapt the kernel module of the previous exercise to receive two or three parameters of your choice. These parameters will be displayed in the console when the module is loaded.
]
```bash
|> modprobe mymodule
[ 3583.616662] Linux module skeleton ex02 loaded
|> dmesg | tail -5
[ 3559.279143] number: 1
[ 3581.198562] Linux module skeleton unloaded
[ 3583.616662] Linux module skeleton ex03 loaded
[ 3583.621085] text: The answer to the Ultimate Question of Life, The Universe, and Everything
[ 3583.621085] number: 42
|> modprobe -r mymodule
[ 3588.404778] Linux module skeleton unloaded
```

33
doc/lab01-module/ex03.typ Normal file
View File

@@ -0,0 +1,33 @@
#import "/doc/metadata.typ": *
=== What does it mean the 4 values in ```/proc/sys/kernel/printk``` ? <lab01:ex03>
We can show what there is in:
```bash
|> cat /proc/sys/kernel/printk
7 4 1 7
```
The number specified the level of output in a console.
This file specifies the log level for: \
current (7), default (4), minimum (1) and boot-time default (7).
This number matches with this table (#link("https://www.kernel.org/doc/html/latest/core-api/printk-basics.html", [printk documentation])):
#table(
columns: (2fr, 1fr, 3fr),
[*Name*], [*String*], [*Alias function*],
[KERN_EMERG], ["0"], [pr_emerg()],
[KERN_ALERT], ["1"], [pr_alert()],
[KERN_CRIT], ["2"], [pr_crit()],
[KERN_ERR], ["3"], [pr_err()],
[KERN_WARNING], ["4"], [pr_warning()],
[KERN_NOTICE], ["5"], [pr_notice()],
[KERN_INFO], ["6"], [pr_info()],
[KERN_DEBUG], ["7"], [pr_debug() and pr_devel() if DEBUG is defined],
[KERN_DEFAULT], [""], [],
[KERN_CONT], ["c"], [pr_cont()],
)

25
doc/lab01-module/ex04.typ Normal file
View File

@@ -0,0 +1,25 @@
#import "/doc/metadata.typ": *
=== Create module with dynamic allocation and a chained list <lab01:ex04>
#colorbox(title: "Exercise", color: hei-blue)[
Create dynamically elements in the kernel. Adapt a kernel module to specify at the installation the number of element to create a initial text.
Each element will contain a unique number. The elements are create at the installation of the module adn chained in a list.
These elements will be destruct during the uninstallation of the module.
Some information messages are emits to allow debugging.
]
To allocate memory in the kernel, we can use the `kcalloc` function. It allows to allocate directly the memory for all element. It's also possible to use `kzalloc` in a loop to allocate memory for each element. We prefer allocate all the memory at once to avoid fragmentation and to be sure all the memory can be allocated.
```bash
struct element* element_ptr = kcalloc(elements, sizeof(struct element), GFP_KERNEL);
for (int i = 0; i < elements; i++) {
struct element* e = element_ptr + i;
if (e != 0) {
strncpy(e->text, text, TEXT_LENGTH_MAX - 1);
e->unique_number = i;
list_add_tail(&e->node, &list_unique_elements);
pr_info ("add element %d: %s\n", e->unique_number, e->text);
}
}
```

33
doc/lab01-module/ex05.typ Normal file
View File

@@ -0,0 +1,33 @@
#import "/doc/metadata.typ": *
=== Display the processor chip ID, CPU temperature and the MAC adress of the Ethernet controller <lab01:ex05>
#colorbox(title: "Exercise", color: hei-blue)[
- Chip ID registers: _0x01c1'4200_ to _0x01c1'420c_
- 32 bits register of the temperature sensor: _0x01c2'5080_
- two 32 bits registers of the Ethernet controller MAC address: _0x01c3'0050_ and _0x01c3'0054_
To calculate the temperature value, there is this formul:
$
"temperature" = -1991 dot "register value" / 10 + 223000
$
The chip ID can be verified in ```/proc/iomem```.
The register value of the temperature can be verified in the file: ```/sys/class/thermal/thermal_zone0/temp```.
The MAC address can be verified with ``` ifconfig```.
]
The resources are savec in a struct:
```c
static struct resource* resources[3] = {[0] = 0,};
```
resources[0] is reserved for the chip ID, resources[1] for the temperature sensor and resources[2] for the Ethernet controller.
We first allocate the resources with `request_mem_region` function. Then we can map the physical address to a virtual address with `ioremap` function. Finally, we can read the value of the registers with `ioread32` function. The request fail because we have an overlap with the EEPROM, but we can ignore this error because we can still read the registers with `ioremap` function.
```c
// Request the resource at (CHIP_ID_BASE_ADDR)
resources[0] = request_mem_region(CHIP_ID_BASE_ADDR, 0x1000, "nanopi - chip ID");
// Map the physical address (CHIP_ID_BASE_ADDR) to a virtual address (registers[0])
registers[0] = ioremap(CHIP_ID_BASE_ADDR, 0x1000);
```

View File

@@ -0,0 +1,8 @@
#import "/doc/metadata.typ": *
=== Kernel thread <lab01:ex06>
#colorbox(title: "Exercise", color: hei-blue)[
Develop a module which allows to instanciate a thread in the kernel. This thread will display a message every 5 seconds. Use the function ```ssleep(5)``` to sleep the thread from ``` linux/delay.h```.
]
Easy exercice, a thread in the kernel is a `struct task_struct*` that can be created with `kthread_run`

15
doc/lab01-module/ex07.typ Normal file
View File

@@ -0,0 +1,15 @@
#import "/doc/metadata.typ": *
=== Sleeping <lab01:ex07>
#colorbox(title: "Exercise", color: hei-blue)[
Develop a module which instanciate 2 threads in the kernel. The first one will wait a wake up notification from the second thread and will sleep. The second will send the notification every 5 seconds. Then it will sleep. We will use the waitqueue for the sleeping function. To allow debugging, each thread will send a message when it wakes up.
]
This exercice make 2 threads in concurrency with wait queue. Here the queue ware declare
statically with the macro `DECLARE_WAIT_QUEUE_HEAD`. Then for this exercice we use an atomic
trigger with 2 queues. It important that the trigger is atomic or protected by mutex because
there is concurrency. The wait queues are used to wait until the trigger has changed to keep
synchronization between the threads.
It is very important to add `kthread_should_stop()` as a condition to wake up queue, because if there is
a problem during the implementation, we cannot kill the code.

24
doc/lab01-module/ex08.typ Normal file
View File

@@ -0,0 +1,24 @@
#import "/doc/metadata.typ": *
=== Interrupts <lab01:ex08>
#colorbox(title: "Exercise", color: hei-blue)[
Develop a module which allows to detect every push on the button of the nanopi with interrupt. Every interrupts will send a message for debugging.
- Use the service ``` gpio_request(<io_nr>, <label>)```
- Get the interrupt vector with ``` gpio_to_irq(<io_nr>)```
- Extension card information:
- k1 - gpio: A, pin_nr=0, io_nr=0
- k2 - gpio: A, pin_nr=2, io_nr=2
- k3 - gpio: A, pin_nr=3, io_nr=3
]
We made a custom structur for the gpio device that contain all useful information like the name and the id.
```c
struct gpio_nanopi {
int id;
char* name;
};
static struct gpio_nanopi switchK1 = {0, "K1: GPIOA.0"};
static struct gpio_nanopi switchK2 = {2, "K2: GPIOA.2"};
static struct gpio_nanopi switchK3 = {3, "K3: GPIOA.3"};
```

79
doc/lab01-module/main.typ Normal file
View File

@@ -0,0 +1,79 @@
#import "/doc/metadata.typ": *
#let ln(num) = {
let str_num = if int(num) < 10 { "0" + str(num) } else { str(num) }
let lbl = label("lab01:ex" + str_num)
link(lbl)[Ex 1.#num]
}
= Linux Kernel Programming
In this lab, we learn how to develop a tiny kernel module. We initially create a tiny skeleton that just print a message when the module is loaded and unloaded in #ln(1). Then in #ln(2), we see how to use parameters with insmod and with modprobe. To make things easier for us, weve added a line to the makefile that copy the modules configuration file (that contain the parameters for the modules) to the correct directory on the target. The `install` command is used as a combination of `mkdir`, `cp` and `chmod`.
```makefile
install:
$(MAKE) -C $(KDIR) M=$(PWD) INSTALL_MOD_PATH=$(MODPATH) modules_install
install -D -m 0644 $(SOURCE).conf $(MODPATH)/etc/modprobe.d/$(SOURCE).conf
```
The exercise 3 ask us what does it mean the 4 values in `/proc/sys/kernel/printk`?
We can show what there is in:
```bash
|> cat /proc/sys/kernel/printk
7 4 1 7
```
The number specified the level of output in a console.
This file specifies the log level for: \
current (7), default (4), minimum (1) and boot-time default (7).
This number matches with this table (#link("https://www.kernel.org/doc/html/latest/core-api/printk-basics.html", [printk documentation])):
#table(
columns: (2fr, 1fr, 3fr),
[*Name*], [*String*], [*Alias function*],
[KERN_EMERG], ["0"], [pr_emerg()],
[KERN_ALERT], ["1"], [pr_alert()],
[KERN_CRIT], ["2"], [pr_crit()],
[KERN_ERR], ["3"], [pr_err()],
[KERN_WARNING], ["4"], [pr_warning()],
[KERN_NOTICE], ["5"], [pr_notice()],
[KERN_INFO], ["6"], [pr_info()],
[KERN_DEBUG], ["7"], [pr_debug() and pr_devel() if DEBUG is defined],
[KERN_DEFAULT], [""], [],
[KERN_CONT], ["c"], [pr_cont()],
)
In #ln(4), we see how to dynamically create elements in the kernel. We use `kcallo` instead of `kzalloc` to allocate all the memory at once and be certain we have the necessary place for all elements of our module. It also a better approach in our opinion to avoid fragmentation.
We spent some time on the #ln(5) to understand that the `request_mem_region` failed because we have an overlap with the EEPROM.
The #ln(6) was a straightforward exercise where we had to develop a module that instantiated a thread.
In the #ln(7) was on concurrency. We had 2 threads with a wait queue. We learn how to suspend a thread, how to wake it up and how to do atomic operation.
In the last exercise of this lab, #ln(8), we see how to manage interruptions and connect them to a gpio.
== Cheat sheet commands
- `modinfo <module.ko>`: display information about a kernel module
- `insmod <module.ko>`: install a kernel module (without checking for dependencies)
- `rmmod <module.ko>`: uninstall a kernel module
- `lsmod`: list the currently loaded kernel modules
- `dmesg`: display the kernel log
- `cat /proc/modules`: display the currently loaded kernel modules with more details
- `modprobe <module>`: install a kernel module and its dependencies
- `modprobe -r <module>`: uninstall a kernel module and its dependencies
- `make`: build the kernel module
- `make install`: install the kernel module in the root filesystem
== Zed
For this lab, we start to work with another code editor than vscode. Not because we don't like Microsoft, ... but mostly for this reason. We use zed with the new devcontainer implementation on this wonderful code editor. To be able to work in nice condition, we add our own `.clangd` build with the help `bear`.
This clangd, allow us to have a perfect autocompletion and a enjoyable code navigation. We can easily jump to the definition of a function and see the documentation of a function.
Thanks to Zed teams for this awesome code editor and \@Fastium for his clangd
== Conclusion
All this lab was done by iteration on the initial skeleton. We develop everything in the #link("https://github.com/Klagarge/MSE-MA-CSEL/tree/main/src/01-skeleton")[src/01-skeleton] folder.
It was a very delightful introduction lab that show us some possibilities when we want to create a kernel module. Everything was new for us, so even it's basics concept, this was a bit challenging to grasp the subject.

View File

@@ -0,0 +1 @@
#import "/doc/metadata.typ": *

View File

@@ -0,0 +1 @@
#import "/doc/metadata.typ": *

View File

@@ -0,0 +1 @@
#import "/doc/metadata.typ": *

View File

@@ -0,0 +1 @@
#import "/doc/metadata.typ": *

View File

@@ -0,0 +1 @@
#import "/doc/metadata.typ": *

View File

@@ -0,0 +1 @@
#import "/doc/metadata.typ": *

View File

@@ -0,0 +1,34 @@
#import "/doc/metadata.typ": *
#let ln(num) = {
// let str_num = if int(num) < 10 { "0" + str(num) } else { str(num) }
// let lbl = label("lab02:ex" + str_num)
// link(lbl)[Ex 2.#num]
[Ex 2.#num]
}
= Linux Kernel Programming
In the First exercise, we learn how to access a register thought the `/dev/mem` interface. The purpose was to read the chip ID, but we learn how to access in a specific region of the memory. How pages work and how to map them in the user space.
For exercise 2, we see how to create a character device driver. We learn how to create a device file, how to write a read and write functions and how to test it with `echo` and `cat`. Our module has a `MAJOR` dynamically allocated (but should be 511 with default nanopi installation) and only one minor. To verify the major number, we can use `cat /proc/devices` and look for our module name. To test the module, we need to create a character device file with the right major and minor number.
```bash
mknod /dev/test-device c 511 0 # Create character device
echo "lalalalalaalalalalallala" > /dev/test-device # Write to the device
cat /dev/test-device # Read from the device
```
Quite easy to extend to exercise 3 by adding the parameters as we did in the previous lab. This parameters define the number of minor available.
Exercise 4 is the continuity, we had to create a tiny app that basically do the `echo` and `cat` for us. We can use the `open`, `write`, `read` and `close` system calls to interact with our device file. We still need to create the device file:
```bash
mknod /dev/toto0 c 511 0
```
The next step in exercise 5 is to create a sysfs entry for our module. Lot of theory, but once the theory is grasped, it's relatively straightforward to use the sysfs functions in our module.
The sysfs class is useful when we are attribute oriented. It's easy to store attributes in files. The platform driver is useful when we are processes oriented. The misc device simplify the peripheral instantiation.
== Adaptation for Zed environment
For this lab, we have to work with application and not with module. We have the same problem with clang for the LSP with Zed. To solve it, we include the Linux header files and specify the path of sysroot. Like this, clang have all the dependencies that we need. And tadam, the wonderful environment we had on previous lab is back!
== Conclusion
All the content of this is on #link("https://github.com/Klagarge/MSE-MA-CSEL/tree/main/src/02-driver")[src/02-driver]. It was pleasant to initially see how to manage a character device manually and step by step see how to do it with an easier method. I personally like to start from the bottom.

View File

@@ -0,0 +1,3 @@
#import "/doc/metadata.typ": *
= Linux System Programming

View File

@@ -1,3 +1,7 @@
#import "@preview/hei-synd-thesis:0.4.0": *
#import "/doc/resources/glossary.typ": *
#import "@preview/grape-suite:3.1.0": exercise
#import exercise: task, subtask
//-------------------------------------
// Document options
//
@@ -55,4 +59,4 @@
maxdepth: 3,
)
#let gloss = true
#let gloss = true

View File

@@ -1,5 +1,4 @@
cmake_minimum_required(VERSION 3.28)
project(ex7-app)
include(../../nanopi.cmake)
add_executable(silly_led_control silly_led_control.c)

View File

@@ -1,12 +1,13 @@
# Makefile for CMake project with intelligent configuration
# Default target
all: build/build.ninja
all: build/build.ninja
cmake --build build
# Create build directory and generate build files if needed
build/build.ninja : CMakeLists.txt
cmake -S . -B build -G "Ninja"
cmake -S . -B build -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=../../nanopi.cmake
# Clean build directory
clean:
@@ -16,4 +17,4 @@ clean:
rebuild: clean all
# Phony targets (targets that don't represent files)
.PHONY: all clean rebuild
.PHONY: all clean rebuild

View File

@@ -28,8 +28,7 @@ clean:
install:
$(MAKE) -C $(KDIR) M=$(PWD) INSTALL_MOD_PATH=$(MODPATH) modules_install
install -d $(MODPATH)/etc/modprobe.d
install -m 0644 $(SOURCE).conf $(MODPATH)/etc/modprobe.d/$(SOURCE).conf
install -D -m 0644 $(SOURCE).conf $(MODPATH)/etc/modprobe.d/$(SOURCE).conf
endif

View File

@@ -0,0 +1,15 @@
#include <linux/module.h> // needed by all modules
#include <linux/init.h> // needed for macros
#include <linux/kernel.h> // needed for debugging
#include <linux/moduleparam.h>
static char* text = "dummy text";
module_param(text, charp, 0664);
static int elements = 1;
module_param(elements, int, 0);
void parameters_print(void) {
pr_debug("text: %s\n", text);
pr_debug("elements: %d\n", elements);
}

View File

@@ -0,0 +1,58 @@
#include <linux/module.h> // needed by all modules
#include <linux/init.h> // needed for macros
#include <linux/kernel.h> // needed for debugging
#include <linux/slab.h> // dynamic memory allocation
#include <linux/list.h> // linked list
#include <linux/string.h>
#define TEXT_LENGTH_MAX 255
struct element {
char text[TEXT_LENGTH_MAX];
int32_t unique_number;
struct list_head node;
};
static LIST_HEAD (list_unique_elements);
void dynAlloc_init(void) {
pr_info("Initialize dynamic allocation and linked list\n");
struct element* element_ptr = kcalloc(elements, sizeof(struct element), GFP_KERNEL);
if (element_ptr == 0) {
pr_err("Failed to allocate memory for %d elements\n", elements);
return;
}
uint8_t i;
const uint8_t length = TEXT_LENGTH_MAX - 1;
for (i = 0; i < elements; i++) {
struct element* e = element_ptr + i;
if (e != 0) {
strncpy(e->text, text, length);
e->unique_number = i;
list_add_tail(&e->node, &list_unique_elements);
pr_info ("add element %d: %s\n", e->unique_number, e->text);
}
}
pr_info("Dynamic allocation and linked list initialized\n");
}
void dynAlloc_exit(void) {
pr_info("Free memory allocated for dynamic allocation and linked list\n");
struct element* e;
while (!list_empty(&list_unique_elements)) {
e = list_entry(list_unique_elements.next, struct element, node);
pr_info ("delete element %d: %s\n", e->unique_number, e->text);
list_del(&e->node);
if (e != 0) {
kfree(e);
}
}
pr_info("Memory allocated for dynamic allocation and linked list freed\n");
}

View File

@@ -0,0 +1,105 @@
#include <linux/module.h> // needed by all modules
#include <linux/init.h> // needed for macros
#include <linux/kernel.h> // needed for debugging
#include <linux/ioport.h>
#include <linux/io.h>
#define CHIP_ID_BASE_ADDR 0x01c14000
#define TEMPERATURE_SENSOR_BASE_ADDR 0x01C25000
#define ETHERNET_CONTROLLER_BASE_ADDR 0x01C30000
static struct resource* resources[3] = {[0] = 0,};
void ioMemoryMapped_init(void) {
pr_info("Initialize memory-mapped I/O\n");
// Declare variables
unsigned char* registers[3] = {[0] = 0,};
uint32_t chipid[4] = {[0] = 0,};
uint32_t temperature = 0;
uint32_t mac_address[2] = {[0] = 0,};
// Request memory
resources[0] = request_mem_region(CHIP_ID_BASE_ADDR, 0x1000, "nanopi - chip ID");
if (resources[0] == 0) {
pr_info("Failed to reserve memory region for chip ID\n");
}
resources[1] = request_mem_region(TEMPERATURE_SENSOR_BASE_ADDR, 0x1000, "nanopi - temperature sensor");
if (resources[1] == 0) {
pr_info("Failed to reserve memory region for temperature sensor\n");
}
resources[2] = request_mem_region(ETHERNET_CONTROLLER_BASE_ADDR, 0x1000, "nanopi - Ethernet controller");
if (resources[2] == 0) {
pr_info("Failed to reserve memory region for Ethernet controller\n");
}
// Map memory
registers[0] = ioremap(CHIP_ID_BASE_ADDR, 0x1000);
if (registers[0] == 0) {
pr_err("Failed to map processor registers for chip ID\n");
return;
}
registers[1] = ioremap(TEMPERATURE_SENSOR_BASE_ADDR, 0x1000);
if (registers[1] == 0) {
pr_err("Failed to map processor registers for temperature sensor\n");
return;
}
registers[2] = ioremap(ETHERNET_CONTROLLER_BASE_ADDR, 0x1000);
if (registers[2] == 0) {
pr_err("Failed to map processor registers for Ethernet controller\n");
return;
}
// Read values - Chip ID
chipid[0] = ioread32(registers[0] + 0x200);
chipid[1] = ioread32(registers[0] + 0x204);
chipid[2] = ioread32(registers[0] + 0x208);
chipid[3] = ioread32(registers[0] + 0x20c);
pr_info(
"chipid=%08x'%08x'%08x'%08x\n",
chipid[0], chipid[1], chipid[2], chipid[3]
);
// Read values - Temperature
temperature = -1191 * (int32_t) ioread32(registers[1] + 0x80) / 10 + 223000;
pr_info(
"temperature=%d (register value: %d)\n",
temperature, ioread32(registers[1] + 0x80)
);
// Read values - MAC address
mac_address[0] = ioread32(registers[2] + 0x50);
mac_address[1] = ioread32(registers[2] + 0x54);
pr_info(
"mac-addr=%02x:%02x:%02x:%02x:%02x:%02x\n",
(mac_address[1] >> 0) & 0xff,
(mac_address[1] >> 8) & 0xff,
(mac_address[1] >> 16) & 0xff,
(mac_address[1] >> 24) & 0xff,
(mac_address[0] >> 0) & 0xff,
(mac_address[0] >> 8) & 0xff
);
// Unmap memory
iounmap(registers[0]);
iounmap(registers[1]);
iounmap(registers[2]);
pr_info("Memory-mapped I/O initialized\n");
}
void ioMemoryMapped_exit(void) {
pr_info("Free memory-mapped I/O\n");
// Release memory
if (resources[0] != 0) release_mem_region(CHIP_ID_BASE_ADDR, 0x1000);
if (resources[1] != 0) release_mem_region(TEMPERATURE_SENSOR_BASE_ADDR, 0x1000);
if (resources[2] != 0) release_mem_region(ETHERNET_CONTROLLER_BASE_ADDR, 0x1000);
}

View File

@@ -0,0 +1,42 @@
#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>
static struct task_struct* sample_thread;
#define DELAY_S 5
int thread_skeletonThread (void* data) {
pr_info("Thread started\n");
while (!kthread_should_stop()) {
pr_info("PING!\n");
ssleep(DELAY_S);
}
return 0;
}
void thread_init(void) {
pr_info("Initialize kernel thread\n");
sample_thread = kthread_run(thread_skeletonThread, NULL, "The Machine that goes");
if (IS_ERR(sample_thread)) {
pr_err("Failed to create kernel thread\n");
return;
}
pr_info("Kernel thread initialized\n");
}
void thread_exit(void) {
pr_info("Exiting kernel thread\n");
kthread_stop(sample_thread);
pr_info("Kernel thread exited\n");
}

View File

@@ -0,0 +1,90 @@
#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/wait.h>
#include <linux/atomic.h>
#define TIMEOUT_S 5
DECLARE_WAIT_QUEUE_HEAD(queue_1);
DECLARE_WAIT_QUEUE_HEAD(queue_2);
static struct task_struct* thread_1;
static struct task_struct* thread_2;
static atomic_t trigger = ATOMIC_INIT(0);
int thread_skeleton_1(void* data) {
// must wait 5 seconds and start thread 2
pr_info("Thread 1 started\n");
while (!kthread_should_stop()) {
pr_info("Thread 1 wakes up\n");
ssleep(TIMEOUT_S);
// Setup trigger for condition of the thread 2
atomic_set(&trigger, 1);
// Wake up thread 2
wake_up_interruptible(&queue_2);
// Wait until thread 2 has reset the trigger
wait_event_interruptible(queue_1, atomic_read(&trigger) == 0 || kthread_should_stop());
}
return 0;
}
int thread_skeleton_2(void* data) {
// have to PING when wakes up
pr_info("Thread 2 started\n");
while (!kthread_should_stop()) {
// wait until trigger is set up
wait_event_interruptible(queue_2, atomic_read(&trigger) == 1 || kthread_should_stop());
pr_info("Thread 2 wakes up\n");
// reset trigger
atomic_set(&trigger, 0);
// wake up thread 1
wake_up_interruptible(&queue_1);
}
return 0;
}
void sleeping_init(void) {
pr_info("Initialize kernel thread\n");
atomic_set(&trigger, 0);
thread_1 = kthread_run(thread_skeleton_1, NULL, "Thread 1 - sleeping");
if (IS_ERR(thread_1)) {
pr_err("Failed to create kernel thread 1\n");
return;
}
thread_2 = kthread_run(thread_skeleton_2, NULL, "Thread 2 - sleeping");
if (IS_ERR(thread_2)) {
pr_err("Failed to create kernel thread 1\n");
return;
}
pr_info("Kernel thread sleeping initialized\n");
}
void sleeping_exit(void) {
pr_info("Exiting kernel sleeping thread\n");
kthread_stop(thread_1);
kthread_stop(thread_2);
pr_info("Kernel thread sleeping exited\n");
}

View File

@@ -0,0 +1,80 @@
#include <linux/module.h> // needed by all modules
#include <linux/init.h> // needed for macros
#include <linux/kernel.h> // needed for debugging
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include "linux/printk.h"
struct gpio_nanopi {
int id;
char* name;
};
static struct gpio_nanopi switchK1 = {0, "K1: GPIOA.0"};
static struct gpio_nanopi switchK2 = {2, "K2: GPIOA.2"};
static struct gpio_nanopi switchK3 = {3, "K3: GPIOA.3"};
irqreturn_t isrGPIO(int irq, void* gpio_struct) {
struct gpio_nanopi *gpio = (struct gpio_nanopi *)gpio_struct;
pr_info("GPIO pressed: %s\n", gpio->name);
return IRQ_HANDLED;
}
void interrupt_init(void) {
pr_info("Initializing interrupts\n");
int status = 0;
// Switch k1
if (gpio_request(switchK1.id, switchK1.name) == 0) {
status = request_irq(
gpio_to_irq(switchK1.id),
isrGPIO,
IRQF_TRIGGER_FALLING | IRQF_SHARED,
switchK1.name,
&switchK1
);
}
// Switch k2
if (gpio_request(switchK2.id, switchK2.name) == 0) {
status = request_irq(
gpio_to_irq(switchK2.id),
isrGPIO,
IRQF_TRIGGER_FALLING | IRQF_SHARED,
switchK2.name,
&switchK2
);
}
// Switch k3
if (gpio_request(switchK3.id, switchK3.name) == 0) {
status = request_irq(
gpio_to_irq(switchK3.id),
isrGPIO,
IRQF_TRIGGER_FALLING | IRQF_SHARED,
switchK3.name,
&switchK3
);
}
pr_info("Interrupt initialized\n");
}
void interrupt_exit(void) {
pr_info("Exiting interrupts\n");
gpio_free(switchK1.id);
free_irq(gpio_to_irq(switchK1.id), &switchK1);
gpio_free(switchK2.id);
free_irq(gpio_to_irq(switchK2.id), &switchK2);
gpio_free(switchK3.id);
free_irq(gpio_to_irq(switchK3.id), &switchK3);
pr_info ("Interrupt exited\n");
}

View File

@@ -3,29 +3,112 @@
#include <linux/init.h> // needed for macros
#include <linux/kernel.h> // needed for debugging
#include <linux/moduleparam.h> // needed for module parameters
#include "linux/printk.h"
#include "kernel-module/s02e02-parameters.c"
// #define PARAMETERS
static char* text = "dummy text";
module_param(text, charp, 0664);
static int number = 1;
module_param(number, int, 0);
#include "kernel-module/s02e04-dynamic_allocation.c"
// #define DYNAMIC_ALLOCATION
static int __init skeleton_init(void)
{
pr_info ("Linux module skeleton ex02 loaded\n");
pr_debug (" text: %s\n number: %d\n", text, number);
#include "kernel-module/s02e05-io_memory_mapped.c"
// #define IO_MEMORY_MAPPED
#include "kernel-module/s02e06-thread.c"
// #define THREAD
#include "kernel-module/s02e07-sleeping.c"
// #define SLEEPING
#include "kernel-module/s02e08-interrupt.c"
#define INTERRUPT
static int __init skeleton_init(void) {
pr_info("Linux module skeleton ex05 loading...\n");
// Lab02 - Exercise 2: Parameters
#ifdef PARAMETERS
pr_info("--------------------\n");
parameters_print();
#endif
// Lab02 - Exercise 4: Dynamic memory allocation and linked list
#ifdef DYNAMIC_ALLOCATION
pr_info("--------------------\n");
Alloc_init();
#endif
// Lab02 - Exercise 5: Memory-mapped I/O
#ifdef IO_MEMORY_MAPPED
pr_info("--------------------\n");
ioMemoryMapped_init();
#endif
// Lab02 - Exercise 6: Kernel thread
#ifdef THREAD
pr_info("--------------------\n");
thread_init();
#endif
// Lab02 - Exercise 7: Sleeping
#ifdef SLEEPING
pr_info("--------------------\n");
sleeping_init();
#endif
// Lab02 - Exercise 8: Interrupt
#ifdef INTERRUPT
pr_info("--------------------\n");
interrupt_init();
#endif
pr_info("--------------------\n");
pr_info("Linux module skeleton loaded\n");
return 0;
}
static void __exit skeleton_exit(void)
{
pr_info ("Linux module skeleton unloaded\n");
static void __exit skeleton_exit(void) {
pr_info("Linux module skeleton unloading...\n");
// Lab02 - Exercise 4: Dynamic memory allocation and linked list
#ifdef DYNAMIC_ALLOCATION
pr_info("--------------------\n");
dynAlloc_exit();
#endif
// Lab02 - Exercise 5: Memory-mapped I/O
#ifdef IO_MEMORY_MAPPED
pr_info("--------------------\n");
ioMemoryMapped_exit();
#endif
// Lab02 - Exercise 6: Kernel thread
#ifdef THREAD
pr_info("--------------------\n");
thread_exit();
#endif
// Lab02 - Exercise 7: Sleeping
#ifdef SLEEPING
pr_info("--------------------\n");
sleeping_exit();
#endif
// Lab02 - Exercise 8: Interrupt
#ifdef INTERRUPT
pr_info("--------------------\n");
interrupt_exit();
#endif
pr_info("--------------------\n");
pr_info ("Linux module skeleton unloaded\n");
}
module_init (skeleton_init);
module_exit (skeleton_exit);
module_init(skeleton_init);
module_exit(skeleton_exit);
MODULE_AUTHOR ("Fastium <fastium.pro@proton.me>");
MODULE_AUTHOR ("Klagarge <remi@heredero.ch>");
MODULE_DESCRIPTION ("Module skeleton");
MODULE_LICENSE ("GPL");
MODULE_AUTHOR("Fastium <fastium.pro@proton.me>");
MODULE_AUTHOR("Klagarge <remi@heredero.ch>");
MODULE_DESCRIPTION("Module skeleton");
MODULE_LICENSE("GPL");

View File

@@ -1 +1 @@
options mymodule number=42 text="The answer to the Ultimate Question of Life, The Universe, and Everything"
options mymodule elements=5 text="Some element"

13
src/02-driver/.clangd Normal file
View File

@@ -0,0 +1,13 @@
CompileFlags:
Add:
# Architecture and cross-compilation
- "--target=aarch64-linux-gnu"
# Setup sysroot for buildroot
- "--sysroot=/buildroot/output/host/aarch64-buildroot-linux-gnu/sysroot"
# Add specific header of linux from buildroot
- "-I/buildroot/output/build/linux-headers-5.15.148/include"
- "-I/buildroot/output/build/linux-headers-5.15.148/arch/arm64/include"
- "-I/buildroot/output/build/linux-headers-5.15.148/arch/arm64/include/generated"
- "-I/buildroot/output/build/linux-headers-5.15.148/**"

44
src/02-driver/Makefile Normal file
View File

@@ -0,0 +1,44 @@
EXE=app
SRCS=$(wildcard *.c)
ifeq ($(target),)
target=nano
endif
CFLAGS=-Wall -Wextra -g -c -O0 -MD -std=gnu11
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)
CC=$(TOOLCHAIN)gcc
LD=$(TOOLCHAIN)gcc
AR=$(TOOLCHAIN)ar
STRIP=$(TOOLCHAIN)strip
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 *~
clean_all: clean
rm -Rf .obj $(EXE) $(EXE)_s $(EXE)_a $(EXE)_a_s $(EXE)_h $(EXE)_h_s
-include $(OBJS:.o=.d)
.PHONY: all clean clean_all

View File

@@ -0,0 +1,33 @@
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"

View File

@@ -0,0 +1,22 @@
# Part executed when called from kernel build system:
ifneq ($(KERNELRELEASE),)
obj-m += mymodule.o ## name of the generated module
mymodule-objs := skeleton.o ## list of objects needed for that module
# Part executed when called from standard make in module source directory:
else
include ../../buildroot_path
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
install:
$(MAKE) -C $(KDIR) M=$(PWD) INSTALL_MOD_PATH=$(MODPATH) modules_install
endif

View File

@@ -0,0 +1,185 @@
#include <linux/module.h> // needed by all modules
#include <linux/moduleparam.h>
#include <linux/init.h> // needed for macros
#include <linux/kernel.h> // needed for debugging
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/minmax.h>
#include <stddef.h>
#include <linux/uaccess.h>
#include <linux/slab.h> // dynamic memory allocation
// linux theory: https://linux-kernel-labs.github.io/refs/heads/master/labs/device_drivers.html
#define MY_MAJOR 42
#define MY_MAX_MINORS 5
#define BUFFER_SIZE 300
// setup as argument the number of buffer available in the device
static int instances = 3;
module_param(instances, int, 0);
struct my_device_data {
dev_t dev_t;
struct cdev cdev;
/* my data starts here */
char** buffers;
};
struct my_device_data devs;
// inode: https://www.kernel.org/doc/html/latest/filesystems/ext4/inodes.html
// file: https://docs.kernel.org/filesystems/api-summary.html#c.file
int skeleton_open(struct inode* i, struct file* f) {
pr_info("Open file \n major:%d\n minor:%d\n",
imajor(i),
iminor(i));
if (iminor(i) >= instances) {
return -EFAULT;
}
if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) != 0) {
pr_info("skeleton : opened for reading & writing...\n");
} else if ((f->f_mode & FMODE_READ) != 0) {
pr_info("skeleton : opened for reading...\n");
} else if ((f->f_mode & FMODE_WRITE) != 0) {
pr_info("skeleton : opened for writing...\n");
}
// Get the stuct of my data
struct my_device_data *my_data = container_of(i->i_cdev, struct my_device_data, cdev);
// point private data on driver data, here this is a char buffer
// point on the right minor buffer
f->private_data = my_data->buffers[iminor(i)];
return 0;
}
int skeleton_release(struct inode* i, struct file* f) {
pr_info("Release file\n");
return 0;
}
ssize_t skeleton_read(struct file* f, char* __user buf, size_t count, loff_t* off) {
pr_info("Read file\n");
if (*off >= BUFFER_SIZE) {
return 0; // End of the file
}
ssize_t len = min((size_t)(BUFFER_SIZE - *off), count);
if (copy_to_user(buf, f->private_data + *off, len)) {
pr_info("Failed to copy to user space buffer\n");
return -EFAULT;
}
*off += len;
return len;
}
ssize_t skeleton_write(struct file* f, const char* __user buf, size_t count, loff_t* off) {
pr_info("Write file\n");
if (*off >= BUFFER_SIZE) {
return -ENOSPC; // No more space in buffer
}
ssize_t len = min((size_t)(BUFFER_SIZE - *off), count);
if (copy_from_user(f->private_data + *off, buf, len)) {
pr_info("Failed to copy from user space buffer\n");
return -EFAULT;
}
*off += len;
return len;
}
static struct file_operations skeleton_fops = {
.owner = THIS_MODULE,
.open = skeleton_open,
.read = skeleton_read,
.write = skeleton_write,
.release = skeleton_release,
};
static int __init skeleton_init(void) {
int ret = 0;
pr_info("My module loading...\n");
pr_info("----------------------\n");
pr_info("Load exercice 3\n");
// ret = register_chrdev_region(MKDEV(MY_MAJOR, 0), instances, "My module"); // register statically
ret = alloc_chrdev_region(&devs.dev_t, 0, instances, "mymodule"); //allocate major and minor
if (ret != 0) {
/* report error */
pr_info("Module registration error: %d\n", ret);
return ret;
}
/* initialize devs fields */
cdev_init(&devs.cdev, &skeleton_fops); // initialize device with files operations
ret = cdev_add(&devs.cdev, devs.dev_t, instances); // notify kernel
if (ret != 0) {
/* report error */
pr_info("cdev add error: %d\n", ret);
return ret;
}
// allocate the array of buffer
int i;
devs.buffers = kzalloc(sizeof(char*) * instances, GFP_KERNEL);
for (i = 0; i < instances; i++) {
devs.buffers[i] = kzalloc(BUFFER_SIZE, GFP_KERNEL);
}
pr_info("----------------------\n");
pr_info("My module is loaded\n");
return ret;
}
static void __exit skeleton_exit(void) {
pr_info("My module unloading...\n");
pr_info("----------------------\n");
cdev_del(&devs.cdev);
unregister_chrdev_region(devs.dev_t, instances);
int i;
for(i=0; i < instances; i++) {
kfree(devs.buffers[i]);
}
kfree(devs.buffers);
pr_info("----------------------\n");
pr_info("My module is unloaded\n");
}
module_init (skeleton_init);
module_exit (skeleton_exit);
MODULE_AUTHOR("Fastium <fastium.pro@proton.me>");
MODULE_AUTHOR("Klagarge <remi@heredero.ch>");
MODULE_DESCRIPTION ("Module pilot character oriented");
MODULE_LICENSE ("GPL");

View File

@@ -0,0 +1,56 @@
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/mman.h>
#include <unistd.h>
int ex_memory_oriented(void) {
int fd = open("/dev/mem", O_RDWR);
if (fd < 0) {
printf("Failed to open /dev/mem: %s\n", strerror(errno));
return 1;
}
size_t page_size = getpagesize(); // return the number of byte in a page
off_t chip_id_addr = 0x01c14200; // physical address
off_t offset = chip_id_addr % page_size; // get the number of page until the chip is address
off_t start_page = chip_id_addr - offset; // align with pages
printf("page_size=0x%x offset=0x%x offset_page=0x%x\n", (unsigned int) page_size, (unsigned int) offset, (unsigned int) start_page);
// Get register virtual address from /dev/mem of the chip id
volatile uint32_t* regs = mmap(0, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, start_page);
if (regs == MAP_FAILED) {
printf("Failed to mmap: %s\n", strerror(errno));
return 1;
}
uint32_t chipid[4] = {[0] = 0,};
// Read values - Chip ID
chipid[0] = *(regs + (0x00 + offset) / sizeof(uint32_t));
chipid[1] = *(regs + (0x04 + offset) / sizeof(uint32_t));
chipid[2] = *(regs + (0x08 + offset) / sizeof(uint32_t));
chipid[3] = *(regs + (0x0c + offset) / sizeof(uint32_t));
printf(
"chipid=%08x'%08x'%08x'%08x\n",
chipid[0], chipid[1], chipid[2], chipid[3]
);
// Free space memory of the user¨
munmap((void*)regs, page_size);
// Close the file
close(fd);
return 0;
}

View File

@@ -0,0 +1,49 @@
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/stat.h>
#define DATA_LENGTH 70
static const char* data = "I've got chocolate stuck to the roof of my mouth, so I can't speak\0";
static char data_read[DATA_LENGTH] = {};
int ex_character_oriented(void) {
printf("Exercice 4 - character oriented\n");
int ret = 0;
const char* path = "/dev/toto0\0";
int fd = open(path, O_RDWR);
if (fd < 0) {
printf("Failed to open /dev/toto0: %s\n (maybe you need to create it)\n", strerror(errno));
return 1;
}
ret = write(fd, data, DATA_LENGTH);
fd = open(path, O_RDWR);
if(ret < 0) {
printf("Failed to write\n");
return 1;
}
ret = read(fd, data_read, DATA_LENGTH);
close(fd);
printf("Read from device: %s\n", path);
printf("Content: %s\n", data_read);
return 0;
}

25
src/02-driver/main.c Normal file
View File

@@ -0,0 +1,25 @@
#include <stdio.h>
#include "exercice/ex1-memory-oriented.c"
// #define MEMORY_ORIENTED
#include "exercice/ex4-character-oriented.c"
#define CHARACTER_ORIENTED
int main() {
int ret = 0;
#ifdef MEMORY_ORIENTED
printf("--------------------------------------\n");
printf("Exercice 1: Memory oriented exercice\n");
ret = ex_memory_oriented();
#endif
#ifdef CHARACTER_ORIENTED
printf("--------------------------------------\n");
ret = ex_character_oriented();
#endif
return ret;
}

View File

@@ -0,0 +1,33 @@
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"

View File

@@ -0,0 +1,22 @@
# Part executed when called from kernel build system:
ifneq ($(KERNELRELEASE),)
obj-m += mymodule.o ## name of the generated module
mymodule-objs := skeleton.o ## list of objects needed for that module
# Part executed when called from standard make in module source directory:
else
include ../../buildroot_path
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
install:
$(MAKE) -C $(KDIR) M=$(PWD) INSTALL_MOD_PATH=$(MODPATH) modules_install
endif

View File

@@ -0,0 +1,60 @@
#include <linux/init.h> /* needed for macros */
#include <linux/kernel.h> /* needed for debugging */
#include <linux/module.h> /* needed by all modules */
#include <linux/cdev.h> /* needed for char device driver */
#include <linux/fs.h> /* needed for device drivers */
#include <linux/uaccess.h> /* needed to copy data to/from user */
#include <linux/device.h> /* needed for sysfs handling */
#include <linux/miscdevice.h>
#include <linux/platform_device.h> /* needed for sysfs handling */
static int val;
ssize_t sysfs_show_val(struct device* dev, struct device_attribute* attr, char* buf) {
pr_info("sysfs_show_val: val=%d\n", val);
sprintf(buf, "%d\n", val);
return strlen(buf);
}
ssize_t sysfs_store_val(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) {
pr_info("sysfs_store_val: buf=%s\n", buf);
val = simple_strtol(buf, 0, 10);
return count;
}
DEVICE_ATTR(val, 0664, sysfs_show_val, sysfs_store_val);
static struct class* sysfs_class;
static struct device* sysfs_device;
static int __init skeleton_init(void) {
int status = 0;
sysfs_class = class_create(THIS_MODULE, "my_sysfs_class");
sysfs_device = device_create(sysfs_class, NULL, 0, NULL, "my_sysfs_device");
if (status == 0) status = device_create_file(sysfs_device, &dev_attr_val);
pr_info("Linux module skeleton loaded\n");
return 0;
}
static void __exit skeleton_exit(void) {
device_remove_file(sysfs_device, &dev_attr_val);
device_destroy(sysfs_class, 0);
class_destroy(sysfs_class);
pr_info("Linux module skeleton unloaded\n");
}
module_init (skeleton_init);
module_exit (skeleton_exit);
MODULE_AUTHOR("Fastium <fastium.pro@proton.me>");
MODULE_AUTHOR("Klagarge <remi@heredero.ch>");
MODULE_DESCRIPTION ("Module pilot charachter oriented");
MODULE_LICENSE ("GPL");

View File

@@ -0,0 +1,13 @@
CompileFlags:
Add:
# Architecture and cross-compilation
- "--target=aarch64-linux-gnu"
# Setup sysroot for buildroot
- "--sysroot=/buildroot/output/host/aarch64-buildroot-linux-gnu/sysroot"
# Add specific header of linux from buildroot
- "-I/buildroot/output/build/linux-headers-5.15.148/include"
- "-I/buildroot/output/build/linux-headers-5.15.148/arch/arm64/include"
- "-I/buildroot/output/build/linux-headers-5.15.148/arch/arm64/include/generated"
- "-I/buildroot/output/build/linux-headers-5.15.148/**"

View File

@@ -0,0 +1,5 @@
EXE=led-controller
SRCS=$(wildcard *.c)
# Include the standard application Makefile for the CSEL1 labs
include ../appl.mk

View File

@@ -0,0 +1,7 @@
build:
make
clean:
rm -rf build
rm -rf .obj
rm led-controller

View File

@@ -0,0 +1,162 @@
/**
* 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 <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include "timer.h"
/*
* status led - gpioa.10 --> gpio10
* power led - gpiol.10 --> gpio362
*/
#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;
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);
// config pin
f = open(GPIO_LED "/direction", O_WRONLY);
write(f, "out", 3);
close(f);
// open gpio value attribute
f = open(GPIO_LED "/value", 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);
}
static void* timer_thread(void* arg) {
ThreadData* data = (ThreadData*)arg;
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);
if (n == -1) {
perror("epoll_wait failed");
break;
}
uint64_t val;
if (read(data->timer_fd, &val, sizeof(val)) != sizeof(val)) {
perror("read timerfd failed");
break;
}
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
led_off(led);
isLedOn = 0;
}
timer_set_time(&data->timer_fd, delay);
}
return NULL;
}
int main(int argc, char* argv[]) {
ThreadData data;
pthread_t thread;
data.flash_period_ms = DEFAULT_TIME_MS;
// Create timerfd
data.timer_fd = timer_create_empty();
timer_set_time(&data.timer_fd, data.flash_period_ms);
// Create epoll instance
data.epoll_fd = epoll_create1(0);
if (data.epoll_fd == -1) {
perror("ERROR while create epoll");
exit(20);
}
timer_link_to_epoll(&data.timer_fd, &data.epoll_fd);
if (pthread_create(&thread, NULL, timer_thread, &data) != 0) {
perror("Failed to create timer thread");
exit(30);
}
pthread_join(thread, NULL);
return 0;
}

View File

@@ -0,0 +1,49 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
int timer_create_empty() {
// Create timerfd
int timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
if (timer_fd == -1) {
perror("timerfd_create failed");
exit(10);
}
return timer_fd;
}
void timer_set_time(int* timer_fd, long period_ms) {
// https://www.man7.org/linux/man-pages/man3/itimerspec.3type.html
struct itimerspec its;
// Periodic interval
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
// Initial expiration
its.it_value.tv_sec = period_ms / 1000;
its.it_value.tv_nsec = (period_ms % 1000) * 1000000;
if (timerfd_settime(*timer_fd, 0, &its, NULL) == -1) {
perror("timerfd_settime failed");
exit(11);
}
}
void timer_link_to_epoll(int* timer_fd, int* epoll_fd) {
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = *timer_fd;
if (epoll_ctl(*epoll_fd, EPOLL_CTL_ADD, *timer_fd, &ev) == -1) {
perror("ERROR while add timerfd to epoll");
exit(21);
}
}

View File

@@ -0,0 +1,4 @@
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);

45
src/appl.mk Normal file
View File

@@ -0,0 +1,45 @@
CFLAGS=-Wall -Wextra -g -c -O0 -MD -std=gnu11
CFLAGS+=$(EXTRA_CFLAGS)
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)
ifeq ($(target),host)
EXEC=$(EXE)_h
endif
CC=$(TOOLCHAIN)gcc
LD=$(TOOLCHAIN)gcc
AR=$(TOOLCHAIN)ar
STRIP=$(TOOLCHAIN)strip
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 *~
clean_all: clean
rm -Rf .obj $(EXE) $(EXE)_s $(EXE)_a $(EXE)_a_s $(EXE)_h $(EXE)_h_s
-include $(OBJS:.o=.d)
.PHONY: all clean clean_all