MODULE_SUPPORTED_DEVICE("Raspberry Pi 3 Model B V1.2");
MODULE_VERSION("0.1");
```
Das Makro `MODULE_LICENSE("GPL");` und die Lizenz-ID "GPL" definiert die Lizenz als `[GNU Public License v2 or later]`.
`MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_SUPPORTED_DEVICE und MODULE_VERSION ` sind Makros für die Modulinformationen welche über den Befehl `modinfo sevenseg.ko`
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 die sgeschieht im folgenden Abschnitt.
Als erstes müssen die Pins initialisiert werden:
```c
voidseven_gpio_init(void){
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);
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.
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
voidseven_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);
printk(KERN_INFO"SEVENSEG: stopping gpio done.");
}
```
Um Zeichen auf der 7-Segment Anzeige ausgeben zu können müssen für jedes Zeichen verschiedene Pins angesprochen und auf `HIGH = 1` oder `LOW = 0` gesetzt werden:
```c
voidseven_status(intdisplay){//Switch-case fuer die Anzeigemoeglichkeiten auf der 7 Segment-Anzeige
switch(display){
case0:
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);
break;
case1:
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);
break;
case2:
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);
break;
case3:
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);
break;
case4:
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);
break;
case5:
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);
break;
case6:
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);
break;
case7:
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);
break;
case8:
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);
break;
case9:
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);
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);
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
und mit `gpio_set_value(unsigned int gpio, int value)` auf HIGH oder LOW gesetzt.
Quelle: [GPIO in the kernel] (https://lwn.net/Articles/532714/)
### 2.2.4 Character Device File
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:
```c
/* Globale Variablen für die Chracter-Device-Schnittstelle */
/* Globale Variablen für die Chracter-Device-Schnittstelle */
staticchar*msg=NULL;
staticintMajor;
```
Die `file_operations` sind im Header linux/fs.h definiert und enthalten Zeiger auf Funktionen, die vom Treiber definiert werden und verschiedene Operationen auf dem Gerät ausführen.
Danach wird der Treiber bzw. das Gerät registriert, dabei wird dem Device-File eine Major Nummer zugewiesen, diese gibt an welcher Treiber welche Gerätedatei verarbeitet.
Man kann seine eigene Major Nummer bestimmen muss aber darauf achten das sie noch nicht vergeben ist. Um dieses Problem zu umgehen gibt man bei der Funktion
`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
intseven_dev_init(void)
{
Major=register_chrdev(0,DEVICE_NAME,&seven_fops);
if(Major<0){
printk(KERN_ALERT"SEVENSEG: Registering char device failed with %d\n",Major);
returnMajor;
}
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
msg=(char*)kmalloc(32,GFP_KERNEL);//Speicherreservierung fuer die Uebertragung von Kernelspace zu Userspace
if(msg!=NULL)
printk("malloc allocator address: 0x%p\n",msg);
returnSUCCESS;
}
```
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
eine Datei mit Majornummer 257 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.
In der folgenden Funktion verwenden wir die bereits erstellte Struktur der write-Funktion:
count=copy_from_user(msg,buffer,len);// Kopieren des Strings aus dem User-Space which open and write this device
if(msg[0]=='1'){
seven_status(1);
}
elseif(msg[0]=='2'){
seven_status(2);
}
elseif(msg[0]=='3'){
seven_status(3);
}
elseif(msg[0]=='4'){
seven_status(4);
}
elseif(msg[0]=='5'){
seven_status(5);
}
elseif(msg[0]=='6'){
seven_status(6);
}
elseif(msg[0]=='7'){
seven_status(7);
}
elseif(msg[0]=='8'){
seven_status(8);
}
else
{
seven_status(0);
}
returnlen;
}
```
Mit der Funktion `memset(void *s, int c, size_t n)` wird die Nachricht aus dem Arbeitsspeicher in den zuvor angelgetnen msg-char geladen und darf eine maximalen Bitlänge von 32 haben
Die Funktion `copy_from_user(void * , const void __user *, unsigned longn);` kopiert die Nachricht aus dem User-Space in den Puffer des Kernel-Space.
Danach wird über die if-Funktionen abgeglichen welchen Wert die Nachricht hat und initialisiert darüber die Funktion `seven_status(display)`.
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.
Zu guter letzt müssen alle Initialisierungsfunktionen über die Modulinitialiesierung aufgerufen werden damit beim Laden des Treibers alle Funktionen garantiert sind:
```c
staticint__initseven_init(void){
printk(KERN_INFO"SEVENSEG: starting...");
seven_gpio_init();
seven_dev_init();
printk(KERN_INFO"SEVENSEG: starting done.");
return0;
}
staticvoid__exitseven_exit(void){
printk(KERN_INFO"SEVENSEG: stopping...");
seven_gpio_exit();
seven_dev_exit();
printk(KERN_INFO"SEVENSEG: stopping done.");
}
module_init(seven_init);//Alle Funktionen in led_init werden im Modul geladen
module_exit(seven_exit);//Alle Funktionen in led_init werden entladen