Update Code authored by Christian Eckl's avatar Christian Eckl
## 2.2 sevenseg.c
### 2.2.1 Header-Files und Definition von Makros
Damit alle Funktionen der im Code verwendeten Funktionen funktionieren müssen einige Header-Files eingebunden werden:
Damit alle der im Code verwendeten Funktionen funktionieren müssen einige Header-Files eingebunden werden:
```
#include <linux/init.h>
#include <linux/module.h>
......@@ -19,21 +19,21 @@ Damit alle Funktionen der im Code verwendeten Funktionen funktionieren müssen e
- linux/kernel.h : Headerfile für die KERN_INFO Ausgabe
```
#define A1 17 // Pin 17 wird durch A1 ersetzt (7SEG a)
#define A2 27 // Pin 27 wird durch A2 ersetzt (7SEG b)
#define A3 22 // Pin 12 wird durch A3 ersetzt (7SEG c)
#define A4 5 // Pin 5 wird durch A4 ersetzt (7SEG d)
#define A5 6 // Pin 6 wird durch A5 ersetzt (7SEG e)
#define A6 13 // Pin 13 wird durch A6 ersetzt (7SEG f)
#define A7 19 // Pin 19 wird durch A7 ersetzt (7SEG g)
#define A8 26 // Pin 26 wird durch A8 ersetzt (7SEG dot)
#define Sa 17 // Pin 17 wird durch Sa ersetzt (7SEG a)
#define Sb 27 // Pin 27 wird durch Sb ersetzt (7SEG b)
#define Sc 22 // Pin 12 wird durch Sc ersetzt (7SEG c)
#define Sd 5 // Pin 5 wird durch Sd ersetzt (7SEG d)
#define Se 6 // Pin 6 wird durch Se ersetzt (7SEG e)
#define Sf 13 // Pin 13 wird durch Sf ersetzt (7SEG f)
#define Sg 19 // Pin 19 wird durch Sg ersetzt (7SEG g)
#define Sp 26 // Pin 26 wird durch Sp ersetzt (7SEG dot)
#define SUCCESS 0
#define DEVICE_NAME "sevenseg" // Device-Name
#define BUF_LEN 1 /* Maximale Länge der Nachricht vom Treiber */
#define BUF_LEN 1 // Maximale Länge einer Nachricht
```
### 2.2.2 Lizensierung und Modul Dokumentation
### 2.2.2 Lizensierung und Modul-Dokumentation
```c
MODULE_LICENSE("GPL");
......@@ -64,43 +64,43 @@ vermagic: 4.9.62-v7+ SMP mod_unload modversions ARMv7 p2v8
Um die 7-Segemntanzeige an einem Raspberry Pi zu betreiben müssen die GPIO-Pins (engl. GPIO – general purpose input/output) über den Treiber angesprochen und verwaltet werden dies geschieht im folgenden Abschnitt.
Als erstes müssen die Pins initialisiert werden:
```c
void seven_gpio_init(void) {
void seven_gpio_init(void) { //Initialisiert die GPIO-Pins (gpio_request)
printk(KERN_INFO "SEVENSEG: starting gpio...");
gpio_request(A1, "A1");
gpio_request(A2, "A2");
gpio_request(A3, "A3");
gpio_request(A4, "A4");
gpio_request(A5, "A5");
gpio_request(A6, "A6");
gpio_request(A7, "A7");
gpio_request(A8, "A8");
gpio_direction_output(A1, 0);
gpio_direction_output(A2, 0);
gpio_direction_output(A3, 0);
gpio_direction_output(A4, 0);
gpio_direction_output(A5, 0);
gpio_direction_output(A6, 0);
gpio_direction_output(A7, 0);
gpio_direction_output(A8, 0);
gpio_request(Sa, "Sa");
gpio_request(Sb, "Sb");
gpio_request(Sc, "Sc");
gpio_request(Sd, "Sd");
gpio_request(Se, "Se");
gpio_request(Sf, "Sf");
gpio_request(Sg, "Sg");
gpio_request(Sp, "Sp");
gpio_direction_output(Sa, 0); //Legt fest das der Pin als Output-Pin ansprechbar ist (gpio_direction_output)
gpio_direction_output(Sb, 0);
gpio_direction_output(Sc, 0);
gpio_direction_output(Sd, 0);
gpio_direction_output(Se, 0);
gpio_direction_output(Sf, 0);
gpio_direction_output(Sg, 0);
gpio_direction_output(Sp, 0);
printk(KERN_INFO "SEVENSEG: starting gpio done.");
}
```
Die GPIO-Pins müssen zuerst für das Modul reserviert werden, dies wird mit `gpio_request(unsigned int gpio, const char *label)` erreicht.
Um mit den Pins arbeiten zu können muss noch festgelegt werden, dass es Output-Pins sind was, mit `gpio_direction_output(unsigned int gpio, int value)` bewerkstelligt wird.
Um mit den Pins arbeiten zu können muss noch festgelegt werden, dass es Output-Pins sind, was mit `gpio_direction_output(unsigned int gpio, int value)` bewerkstelligt wird.
Wenn man das Modul entladen will müssen die GPIO-Pins wieder freigegeben werden was im folgenden Codeabschnitt durch `gpio_free(const int gpio)` realisiert wurde:
```c
void seven_gpio_exit(void) { //Beim Rausladen des LKM werden die Pins wieder freigegeben (gpio_free)
printk(KERN_INFO "SEVENSEG: stopping gpio...");
gpio_free(A1);
gpio_free(A2);
gpio_free(A3);
gpio_free(A4);
gpio_free(A5);
gpio_free(A6);
gpio_free(A7);
gpio_free(A8);
gpio_free(Sa);
gpio_free(Sb);
gpio_free(Sc);
gpio_free(Sd);
gpio_free(Se);
gpio_free(Sf);
gpio_free(Sg);
gpio_free(Sp);
printk(KERN_INFO "SEVENSEG: stopping gpio done.");
}
```
......@@ -112,117 +112,185 @@ void seven_status(int display) { //Switch-case fuer die Anzeigemoeg
switch (display) {
case 0 :
gpio_set_value(A1, 0);
gpio_set_value(A2, 0);
gpio_set_value(A3, 0);
gpio_set_value(A4, 0);
gpio_set_value(A5, 0); // 7-Segment zeigt 0 an
gpio_set_value(A6, 0);
gpio_set_value(A7, 0);
gpio_set_value(A8, 0);
gpio_set_value(Sa, 0);
gpio_set_value(Sb, 0);
gpio_set_value(Sc, 0);
gpio_set_value(Sd, 0);
gpio_set_value(Se, 0); // 7-Segment zeigt 0 an
gpio_set_value(Sf, 0);
gpio_set_value(Sg, 1);
gpio_set_value(Sp, 1);
break;
case 1:
gpio_set_value(A1, 1);
gpio_set_value(A2, 1);
gpio_set_value(A3, 1);
gpio_set_value(A4, 1);
gpio_set_value(A5, 1); // 7-Segment zeigt 1 an
gpio_set_value(A6, 1);
gpio_set_value(A7, 1);
gpio_set_value(A8, 1);
gpio_set_value(Sa, 1);
gpio_set_value(Sb, 0);
gpio_set_value(Sc, 0);
gpio_set_value(Sd, 1);
gpio_set_value(Se, 1); // 7-Segment zeigt 1 an
gpio_set_value(Sf, 1);
gpio_set_value(Sg, 1);
gpio_set_value(Sp, 1);
break;
case 2:
gpio_set_value(A1, 0);
gpio_set_value(A2, 0);
gpio_set_value(A3, 0);
gpio_set_value(A4, 0);
gpio_set_value(A5, 0); // 7-Segment zeigt 2 an
gpio_set_value(A6, 0);
gpio_set_value(A7, 0);
gpio_set_value(A8, 0);
gpio_set_value(Sa, 0);
gpio_set_value(Sb, 0);
gpio_set_value(Sc, 1);
gpio_set_value(Sd, 0);
gpio_set_value(Se, 0); // 7-Segment zeigt 2 an
gpio_set_value(Sf, 1);
gpio_set_value(Sg, 0);
gpio_set_value(Sp, 1);
break;
case 3:
gpio_set_value(A1, 0);
gpio_set_value(A2, 0);
gpio_set_value(A3, 0);
gpio_set_value(A4, 0);
gpio_set_value(A5, 0); // 7-Segment zeigt 3 an
gpio_set_value(A6, 0);
gpio_set_value(A7, 0);
gpio_set_value(A8, 0);
gpio_set_value(Sa, 0);
gpio_set_value(Sb, 0);
gpio_set_value(Sc, 0);
gpio_set_value(Sd, 0);
gpio_set_value(Se, 1); // 7-Segment zeigt 3 an
gpio_set_value(Sf, 1);
gpio_set_value(Sg, 0);
gpio_set_value(Sp, 1);
break;
case 4:
gpio_set_value(A1, 0);
gpio_set_value(A2, 0);
gpio_set_value(A3, 0);
gpio_set_value(A4, 0);
gpio_set_value(A5, 0); // 7-Segment zeigt 4 an
gpio_set_value(A6, 0);
gpio_set_value(A7, 0);
gpio_set_value(A8, 0);
gpio_set_value(Sa, 1);
gpio_set_value(Sb, 0);
gpio_set_value(Sc, 0);
gpio_set_value(Sd, 1);
gpio_set_value(Se, 1); // 7-Segment zeigt 4 an
gpio_set_value(Sf, 0);
gpio_set_value(Sg, 0);
gpio_set_value(Sp, 1);
break;
case 5:
gpio_set_value(A1, 0);
gpio_set_value(A2, 0);
gpio_set_value(A3, 0);
gpio_set_value(A4, 0);
gpio_set_value(A5, 0); // 7-Segment zeigt 5 an
gpio_set_value(A6, 0);
gpio_set_value(A7, 0);
gpio_set_value(A8, 0);
gpio_set_value(Sa, 0);
gpio_set_value(Sb, 1);
gpio_set_value(Sc, 0);
gpio_set_value(Sd, 0);
gpio_set_value(Se, 1); // 7-Segment zeigt 5 an
gpio_set_value(Sf, 0);
gpio_set_value(Sg, 0);
gpio_set_value(Sp, 1);
break;
case 6:
gpio_set_value(A1, 0);
gpio_set_value(A2, 0);
gpio_set_value(A3, 0);
gpio_set_value(A4, 0);
gpio_set_value(A5, 0); // 7-Segment zeigt 6 an
gpio_set_value(A6, 0);
gpio_set_value(A7, 0);
gpio_set_value(A8, 0);
gpio_set_value(Sa, 0);
gpio_set_value(Sb, 1);
gpio_set_value(Sc, 0);
gpio_set_value(Sd, 0);
gpio_set_value(Se, 0); // 7-Segment zeigt 6 an
gpio_set_value(Sf, 0);
gpio_set_value(Sg, 0);
gpio_set_value(Sp, 0);
break;
case 7:
gpio_set_value(A1, 0);
gpio_set_value(A2, 0);
gpio_set_value(A3, 0);
gpio_set_value(A4, 0);
gpio_set_value(A5, 0); // 7-Segment zeigt 7 an
gpio_set_value(A6, 0);
gpio_set_value(A7, 0);
gpio_set_value(A8, 0);
gpio_set_value(Sa, 0);
gpio_set_value(Sb, 0);
gpio_set_value(Sc, 0);
gpio_set_value(Sd, 1);
gpio_set_value(Se, 1); // 7-Segment zeigt 7 an
gpio_set_value(Sf, 1);
gpio_set_value(Sg, 1);
gpio_set_value(Sp, 1);
break;
case 8:
gpio_set_value(A1, 0);
gpio_set_value(A2, 0);
gpio_set_value(A3, 0);
gpio_set_value(A4, 0);
gpio_set_value(A5, 0); // 7-Segment zeigt 8 an
gpio_set_value(A6, 0);
gpio_set_value(A7, 0);
gpio_set_value(A8, 0);
gpio_set_value(Sa, 0);
gpio_set_value(Sb, 0);
gpio_set_value(Sc, 0);
gpio_set_value(Sd, 0);
gpio_set_value(Se, 0); // 7-Segment zeigt 8 an
gpio_set_value(Sf, 0);
gpio_set_value(Sg, 0);
gpio_set_value(Sp, 1);
break;
case 9:
gpio_set_value(A1, 0);
gpio_set_value(A2, 0);
gpio_set_value(A3, 0);
gpio_set_value(A4, 0);
gpio_set_value(A5, 0); // 7-Segment zeigt 9 an
gpio_set_value(A6, 0);
gpio_set_value(A7, 0);
gpio_set_value(A8, 0);
gpio_set_value(Sa, 0);
gpio_set_value(Sb, 0);
gpio_set_value(Sc, 0);
gpio_set_value(Sd, 0);
gpio_set_value(Se, 1); // 7-Segment zeigt 9 an
gpio_set_value(Sf, 0);
gpio_set_value(Sg, 0);
gpio_set_value(Sp, 1);
break;
case 'a':
gpio_set_value(Sa, 1);
gpio_set_value(Sb, 1);
gpio_set_value(Sc, 0);
gpio_set_value(Sd, 0);
gpio_set_value(Se, 0); // 7-Segment zeigt a an
gpio_set_value(Sf, 1);
gpio_set_value(Sg, 0);
gpio_set_value(Sp, 1);
break;
case 'b':
gpio_set_value(Sa, 1);
gpio_set_value(Sb, 1);
gpio_set_value(Sc, 0);
gpio_set_value(Sd, 0);
gpio_set_value(Se, 0); // 7-Segment zeigt b an
gpio_set_value(Sf, 0);
gpio_set_value(Sg, 0);
gpio_set_value(Sp, 1);
break;
case 'c':
gpio_set_value(Sa, 1);
gpio_set_value(Sb, 1);
gpio_set_value(Sc, 1);
gpio_set_value(Sd, 0);
gpio_set_value(Se, 0); // 7-Segment zeigt c an
gpio_set_value(Sf, 1);
gpio_set_value(Sg, 0);
gpio_set_value(Sp, 1);
break;
case 'd':
gpio_set_value(Sa, 1);
gpio_set_value(Sb, 0);
gpio_set_value(Sc, 0);
gpio_set_value(Sd, 0);
gpio_set_value(Se, 0); // 7-Segment zeigt d an
gpio_set_value(Sf, 1);
gpio_set_value(Sg, 0);
gpio_set_value(Sp, 1);
break;
case 'e':
gpio_set_value(Sa, 0);
gpio_set_value(Sb, 1);
gpio_set_value(Sc, 1);
gpio_set_value(Sd, 0);
gpio_set_value(Se, 0); // 7-Segment zeigt e an
gpio_set_value(Sf, 0);
gpio_set_value(Sg, 0);
gpio_set_value(Sp, 1);
break;
case 'f':
gpio_set_value(Sa, 0);
gpio_set_value(Sb, 1);
gpio_set_value(Sc, 1);
gpio_set_value(Sd, 1);
gpio_set_value(Se, 0); // 7-Segment zeigt f an
gpio_set_value(Sf, 0);
gpio_set_value(Sg, 0);
gpio_set_value(Sp, 1);
break;
default:
gpio_set_value(A1, 1);
gpio_set_value(A2, 1);
gpio_set_value(A3, 1);
gpio_set_value(A4, 1);
gpio_set_value(A5, 1); //default alle Pins auf LOW gesetzt
gpio_set_value(A6, 1);
gpio_set_value(A7, 1);
gpio_set_value(A8, 1);
gpio_set_value(Sa, 1);
gpio_set_value(Sb, 1);
gpio_set_value(Sc, 1);
gpio_set_value(Sd, 1);
gpio_set_value(Se, 1); //default alle Pins auf LOW gesetzt
gpio_set_value(Sf, 1);
gpio_set_value(Sg, 1);
gpio_set_value(Sp, 1);
break;
}
}
```
Nachdem die Funktion mit der gewünschten Ausgabe `seven_status(int display)` aufgerufen wurde, werden in der switch-case-Funktion die GPIO-Pins angesprochen
......@@ -234,7 +302,7 @@ Quelle: [GPIO in the kernel] (https://lwn.net/Articles/532714/)
Um später mit dem Treiber im Kernel-Space kommunizieren zu können, wird ein Character Device Driver benötigt, der im Kernel-Space eine Node-Datei erstellt, auf die der User
mit `& echo "display" >> /dev/sevenseg` schreiben kann und das Modul die Eingabe verarbeitet.
Als erstes wird hierzu folgende globale Variablen und Strukturfunktionen:
Als erstes werden hierzu folgende globale Variablen und Strukturfunktionen erstellt:
```c
/* Globale Variablen für die Chracter-Device-Schnittstelle */
static ssize_t seven_write(struct file *, const char *, size_t, loff_t *);
......@@ -256,16 +324,16 @@ Man kann seine eigene Major Nummer bestimmen muss aber darauf achten das sie noc
`register_chrdev(unsigned int major, const char *name, struct file_operations *fops)` bei `unsigned int major` 0 ein und lässt sich dann durch die Rückgabe eine freie Major Nummer zuteilen
wie im folgenden Code-Abschnitt:
```c
int seven_dev_init(void)
int seven_dev_init(void) //Wird aufgerufen wenn das Modul geladen wird
{
Major = register_chrdev(0, DEVICE_NAME, &seven_fops);
Major = register_chrdev(0, DEVICE_NAME, &seven_fops); //Registrierung eines Chardevice
if (Major < 0) {
printk(KERN_ALERT "SEVENSEG: Registering char device failed with %d\n", Major);
return Major;
}
printk(KERN_INFO "SEVENSEG: sevenseg driver loaded with major %d\n", Major);
printk(KERN_INFO "SEVENSEG: >> $ mknod /dev/%s c %d 0\n", DEVICE_NAME, Major); //Info welche Nodedatei mit welchen Parametern erstellt werden soll
printk(KERN_INFO "SEVENSEG: >> $ mknod /dev/%s c %d 0\n", DEVICE_NAME, Major); //Info welche Nodedatei mit welchen parametern erstellt werden soll
msg = (char *)kmalloc(32, GFP_KERNEL); //Speicherreservierung fuer die Uebertragung von Kernelspace zu Userspace
if (msg != NULL)
......@@ -275,11 +343,11 @@ int seven_dev_init(void)
```
Mit der KERN_INFO lassen wir uns hier den genauen Pfad ausgeben in der die Node Datei erstellt werden soll in unserem Fall wird mit
> $ mknod /dev/sevenseg 257 0
> $ mknod /dev/sevenseg 243 0
eine Datei mit Majornummer 257 und Minor-Nummer 0 angelegt. Die Minor Nummer wird nur verwendet wenn ein Treiber mehrere Geräte ansprechen soll.
eine Datei mit Major Nummer 243 und Minor-Nummer 0 angelegt. Die Minor Nummer wird nur verwendet wenn ein Treiber mehrere Geräte ansprechen soll.
Zu guter letzt muss noch Speicher für die Übertragung des Puffers reserviert werden da die Eingaben in die Nodedatei nur indirekt ausgelesen werden können.
Zu guter Letzt muss noch Speicher für die Übertragung des Puffers reserviert werden da die Eingaben in die Node Datei nur indirekt ausgelesen werden können.
In der folgenden Funktion verwenden wir die bereits erstellte Struktur der write-Funktion:
```c
......@@ -290,7 +358,7 @@ static ssize_t seven_write(struct file *filep, const char *buffer, size_t len, l
memset(msg, 0, 32);
count = copy_from_user(msg, buffer, len); // Kopieren des Strings aus dem User-Space which open and write this device
count = copy_from_user(msg, buffer, len); // Kopieren des Strings aus dem User-Space
if (msg[0] == '1') {
seven_status(1);
......@@ -316,9 +384,30 @@ static ssize_t seven_write(struct file *filep, const char *buffer, size_t len, l
else if (msg[0] == '8') {
seven_status(8);
}
else if (msg[0] == '9') {
seven_status(9);
}
else if (msg[0] == 'a') {
seven_status('a');
}
else if (msg[0] == 'b') {
seven_status('b');
}
else if (msg[0] == 'c') {
seven_status('c');
}
else if (msg[0] == 'd') {
seven_status('d');
}
else if (msg[0] == 'e') {
seven_status('e');
}
else if (msg[0] == 'f') {
seven_status('f');
}
else
{
seven_status(0);
seven_status('default');
}
return len;
......@@ -344,13 +433,13 @@ void seven_dev_exit(void)
}
```
Hier wird durch die Funktion `unregister_chrdev(unsigned int major, const char *name)` der Treiber abgemeldet in den Parametern wird die Majornummer und der DEVICE_NAME angegeben.
Hier wird durch die Funktion `unregister_chrdev(unsigned int major, const char *name)` der Treiber abgemeldet, in den Parametern wird die Major Nummer und der DEVICE_NAME angegeben.
Quelle: [Linux Loadable Kernel Module HOWTO] (http://tldp.org/LDP/lkmpg/2.6/html/lkmpg.html#AEN569)
### 2.2.5 Die Modulfunktionen
Zu guter letzt müssen alle Initialisierungsfunktionen über die Modulinitialiesierung aufgerufen werden damit beim Laden des Treibers alle Funktionen garantiert sind:
Zu guter Letzt müssen alle Initialisierungsfunktionen über die Modulinitialisierung aufgerufen werden damit beim Laden des Treibers alle Funktionen garantiert sind:
```c
static int __init seven_init(void) {
printk(KERN_INFO "SEVENSEG: starting...");
......@@ -370,3 +459,11 @@ static void __exit seven_exit(void) {
module_init(seven_init); //Alle Funktionen in led_init werden im Modul geladen
module_exit(seven_exit); //Alle Funktionen in led_init werden entladen
```
Die Makros `module_init(seven_init)` und `module_exit(seven_exit)` ermöglichen es die Initialisierungs-Funktionen seperat in eigene Funktionen zu schreiben. Man muss beachten,
dass die Funktionen `seven_init()` und `seven_exit()` ein __init und __exit in der Deklaration haben und vor den Modulmakros aufgerufen werden müssen, da sonst Probleme beim Kompilieren entstehen.
Quelle: [Linux Loadable Kernel Module HOWTO] (http://tldp.org/HOWTO/Module-HOWTO/index.html)
Die ganze sevenseg.c Datei:
[Gesamter Code](https://mygit.th-deg.de/ce12213/Kernelmodul_Sevenseg/blob/master/Sevenseg/sevenseg.c)