Dezvoltare drivere pentru Linux: Diferență între versiuni

De la YO3ITI
Salt la: navigare, căutare
(Pregătiri)
(Primul exemplu super simplu)
 
(Nu s-au afișat 32 de versiuni intermediare efectuate de același utilizator)
Linia 2: Linia 2:
  
 
Pe măsură ce le adun, o să le și structurez mai bine.
 
Pe măsură ce le adun, o să le și structurez mai bine.
==Pregătiri==
+
==Condiții inițiale==
 
===Verificarea versiunii kernel-ului===
 
===Verificarea versiunii kernel-ului===
 
Cazul platformei mele este un Raspberry Pi 4, versiunea '''c03112''' cu un kernel versiunea '''5.4.51-v7l+''' (pentru lista completă de coduri de versiuni, [https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md vezi aici]):
 
Cazul platformei mele este un Raspberry Pi 4, versiunea '''c03112''' cu un kernel versiunea '''5.4.51-v7l+''' (pentru lista completă de coduri de versiuni, [https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md vezi aici]):
Linia 27: Linia 27:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
===Verificare versiune headere pentru kernel===
+
===Verificarea versiunii antetelor de kernel (kernel headers)===
În mod normal, header-ele kernel, pentru dezvoltare, trebuie să se potrivească cu versiunea de kernel:
+
Fișierele antet (header) sunt necesare compilatorului pentru a verifica dacă o anumită funcție este corect utilizată într-un program. Această verificare se mai numește ''verificarea semnăturii unei funcții''. Pentru verificarea semnăturii funcțiilor nu este necesar codul complet al implementării funcțiilor ci doar definiția lor. Ca atare, limitarea doar la includerea header-elor oferă o mare economie de cod și memorie.
 +
 
 +
În cazul concret al dezvoltării driverelor, header-ele kernelului trebuie să se potrivească cu versiunea de kernel:
 
<syntaxhighlight lang="shell" highlight="4,7,10,13,16">
 
<syntaxhighlight lang="shell" highlight="4,7,10,13,16">
 
tom@rpi-yo3iti:~/c/drivere/Salzman/01 $ apt search linux-header*
 
tom@rpi-yo3iti:~/c/drivere/Salzman/01 $ apt search linux-header*
Linia 60: Linia 62:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Primul exemplu super simplu==
+
==Exemple generice, elemente de bază==
 +
===Primul exemplu super simplu===
 +
În forma cea mai simplă (și care e complet inutilă) un driver are nevoie de cel puțin două funcții: una de inițializare și una de ieșire. În cazul programării driverelor, mesajele nu pot fi logate la consola normală ci doar în spațiul adresabil de kernel, cu ajutorul funcției <code>printk</code>.
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
 
#include <linux/module.h> /* necesar tuturor modulelor */
 
#include <linux/module.h> /* necesar tuturor modulelor */
Linia 70: Linia 74:
  
 
         /*
 
         /*
         * A non 0 return means init module failed; module can't be loaded
+
         * O valoare diferită de zero înseamnă că funcția de inițializare a eșuat; modulul nu poate fi încărcat.
 
         */
 
         */
 
         return 0;
 
         return 0;
Linia 117: Linia 121:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Al doilea exemplu==
+
===Al doilea exemplu===
Metoda modernă de definire pentru metodele <code>init</code> și <code>exit</code> este dată mai jos:
+
Metoda acceptată (modernă) pentru definirea metodelor de inițializare (<code>init</code>) și ieșire (<code>exit</code>) este prezentată mai jos. Se poate alege '''orice''' denumire pentru aceste metode cu condiția de a menționa funcționalitatea prin <code>module_init</code> și <code>module_exit</code> ambele evidențiate în exemplul de mai jos:
<syntaxhighlight lang="c">
+
<syntaxhighlight lang="c" highlight="21,22">
 
/*
 
/*
 
  * hello-2.c - demo pentru module_init() și module_exit()
 
  * hello-2.c - demo pentru module_init() și module_exit()
Linia 157: Linia 161:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Compilarea:
+
====Compilarea:====
 
+
Fișierele existente:
 
<syntaxhighlight lang="shell">
 
<syntaxhighlight lang="shell">
 
tom@rpi-yo3iti:~/c/drivere/Salzman/02 $ ls -lsa
 
tom@rpi-yo3iti:~/c/drivere/Salzman/02 $ ls -lsa
Linia 167: Linia 171:
 
4 -rw-r--r-- 1 root root  448 Jul 28 19:23 hello-2.c
 
4 -rw-r--r-- 1 root root  448 Jul 28 19:23 hello-2.c
 
4 -rw-r--r-- 1 root root  176 Jul 28 19:25 Makefile
 
4 -rw-r--r-- 1 root root  176 Jul 28 19:25 Makefile
 +
</syntaxhighlight>
 +
Compilarea se face cu comanda <code>make</code>. Se observă o eroare la liniile 8 și 10 (evidențiate) datorată lipsei directivei <code>MODULE_LICENSE()</code> prin care se specifică tipulde licențiere al codului:
 +
<syntaxhighlight lang="shell" highlight="8,10">
 
tom@rpi-yo3iti:~/c/drivere/Salzman/02 $ make
 
tom@rpi-yo3iti:~/c/drivere/Salzman/02 $ make
 
make -C /lib/modules/5.4.51-v7l+/build M=/home/tom/c/drivere/Salzman/02 modules
 
make -C /lib/modules/5.4.51-v7l+/build M=/home/tom/c/drivere/Salzman/02 modules
Linia 185: Linia 192:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Al treilea exemplu==
+
===Al treilea exemplu - gestiunea automată a memoriei===
 +
<syntaxhighlight lang="c">
 +
/* 
 +
*  hello-3.c - Exemplu pentru comenzile-macro __init, __initdata și __exit.
 +
*/
 +
#include <linux/module.h> /* Needed by all modules */
 +
#include <linux/kernel.h> /* Needed for KERN_INFO */
 +
#include <linux/init.h> /* Needed for the macros */
 +
 
 +
static int hello3_data __initdata = 3;
 +
 
 +
static int __init hello_3_init(void)
 +
{
 +
printk(KERN_INFO "Hello, world %d\n", hello3_data);
 +
return 0;
 +
}
 +
 
 +
static void __exit hello_3_exit(void)
 +
{
 +
printk(KERN_INFO "Goodbye, world 3\n");
 +
}
  
 +
module_init(hello_3_init);
 +
module_exit(hello_3_exit);
 +
</syntaxhighlight>
 +
Comanda macro <code>__init</code> determină eliberarea memoriei alocată funcției <code>init</code> odată ce funcția de inițializare este completă, doar pentru driverele încorporate în kernel. Echivalentul pentru variabile este <code>__initdata</code>. Acest lucru nu este valabil pentru modulele (driverele) încărcate dinamic. La fel se comportă comanda macro <code>__exit</code>. Driverele incluse în kernel nu necesită funcții de eliberare a memoriei, în timp ce cele dinamice au nevoie de un mecanism de eliberare a memoriei.
 +
Aceste comenzi macro sunt definite în <code>linux/init.h</code>; în cazul meu, calea completă este:
 +
<syntaxhighlight lang="shell">
 +
/usr/src/linux-headers-5.4.51-v7+/include/linux/init.h
 +
</syntaxhighlight>
 +
iată cum arată secțiunea coresponzătoare din <code>init.h</code>:
 +
<syntaxhighlight lang="c">
 +
/* These are for everybody (although not all archs will actually
 +
  discard it in modules) */
 +
#define __init          __section(.init.text) __cold  __latent_entropy __noinitretpoline
 +
#define __initdata      __section(.init.data)
 +
#define __initconst    __section(.init.rodata)
 +
#define __exitdata      __section(.exit.data)
 +
#define __exit_call    __used __section(.exitcall.exit)
 +
</syntaxhighlight>
 +
La boot, un mesaj similar cu: <code> ...Freeing unused kernel memory: 236k freed...</code> înseamnă exact eliberarea memoriei de de aceste module.
 +
<syntaxhighlight lang="c">
 +
</syntaxhighlight>
 +
 +
===Al patrulea exemplu===
 +
În acest exemplu adăugăm comenzile maro care definesc tipul licenței, autorul codului, descrierea etc.
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
 
/*
 
/*
Linia 234: Linia 285:
 
  */
 
  */
 
MODULE_SUPPORTED_DEVICE("testdevice");
 
MODULE_SUPPORTED_DEVICE("testdevice");
 +
</syntaxhighlight>
 +
Aceste informații pot fi afșare cu comanda <code>modinfo</code>:
 +
<b><code>tom@rpi-yo3iti:~/c/drivere/02 $ modinfo hello-4.ko</code></b>
 +
<syntaxhighlight lang="shell">
 +
filename:      /home/tom/c/drivere/Salzman/02/hello-4.ko
 +
description:    Un driver test, demo
 +
author:        Miron Iancu YO3ITI <miancuster at gmail dot com>
 +
license:        GPL
 +
srcversion:    C6368761702CBF9D1D1740D
 +
depends:       
 +
name:          hello_4
 +
vermagic:      5.4.51-v7l+ SMP mod_unload modversions ARMv7 p2v8
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Al patrulea exemplu==
+
===Al cincilea exemplu===
 +
În codul de mai jos se exemplifică cum se pot schimba mesaje cu un modul kernel (driver) din spațiul utilizator, prin intermediul parametrilor introduși cu funcția <code>module_param</code>:
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
 
/*
 
/*
  * hello-5.c - demonstrează modul în care se transmit variabile modulului
+
  * hello-5.c - demo pentru interactivitate = cum se pot schimba mesaje prin parametri
 
  */
 
  */
  
Linia 249: Linia 313:
  
 
MODULE_LICENSE("GPL");
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Miron Iancu, YO3ITI, <miancuster@gmai.com>");
+
MODULE_AUTHOR("Miron Iancu, YO3ITI, <miancuster at gmail dot com>");
  
 
static short int myshort = 1;
 
static short int myshort = 1;
Linia 315: Linia 379:
 
module_exit(hello_5_exit);
 
module_exit(hello_5_exit);
 
</syntaxhighlight>
 
</syntaxhighlight>
 
+
Pentru adăugarea driver-ului se rulează comanda <code>insmod</code> cu parametri. Această metodă de inițializare este deosebit de utilă pentru a obține drivere care pot fi utilizate pentru o plajă largă de parametri de funcționare:
La rularea <code>insmod</code> cu parametri se obține:
 
 
 
 
<syntaxhighlight lang="shell">
 
<syntaxhighlight lang="shell">
 
sudo insmod hello-5.ko mystring="un_exemplu_de_șir" myint=45 myshort=3 mylong=765765 myintArray=-1,2
 
sudo insmod hello-5.ko mystring="un_exemplu_de_șir" myint=45 myshort=3 mylong=765765 myintArray=-1,2
 
</syntaxhighlight>
 
</syntaxhighlight>
 
+
Log-urile de kernel pot fi consultate cu <code>dmesg</code>
Și <code>dmesg</code>
 
 
 
 
<syntaxhighlight lang="shell">
 
<syntaxhighlight lang="shell">
dmesg
 
 
 
[13561.437899] myshort este un număr întreg de dimensiuni mici: 3
 
[13561.437899] myshort este un număr întreg de dimensiuni mici: 3
 
[13561.437938] myint este un număr întreg: 45
 
[13561.437938] myint este un număr întreg: 45
Linia 336: Linia 394:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
==Exemple pe tipuri de drivere==
 +
===Drivere de tip <code>char</code>===
 +
TODO
 +
 +
===Drivere de sistem===
 +
TODO
 
==Link-uri externe==
 
==Link-uri externe==
 
* [https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md Raspberry Pi revision codes]
 
* [https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md Raspberry Pi revision codes]

Versiunea curentă din 30 iulie 2020 15:09

Aceste pagini conțin o colecție de informații necesare în special celor care doresc să programeze diverse pe Linux (sau Raspberry – fiindcă e același lucru). Am început să adun această documentație pentru dezvoltarea driverelor de care am nevoie pentru diferitele proiecte electronice pe Raspberry.

Pe măsură ce le adun, o să le și structurez mai bine.

Condiții inițiale

Verificarea versiunii kernel-ului

Cazul platformei mele este un Raspberry Pi 4, versiunea c03112 cu un kernel versiunea 5.4.51-v7l+ (pentru lista completă de coduri de versiuni, vezi aici):

tom@rpi-yo3iti:~/c/drivere/Salzman/01 $ uname -a
Linux rpi-yo3iti 5.4.51-v7l+ #1327 SMP Thu Jul 23 11:04:39 BST 2020 armv7l GNU/Linux

Sau, prin /proc/version:

tom@rpi-yo3iti:~/c/drivere/Salzman/01 $ cat /proc/version
Linux version 5.4.51-v7l+ (dom@buildbot) (gcc version 4.9.3 (crosstool-NG crosstool-ng-1.22.0-88-g8460611)) #1327 SMP Thu Jul 23 11:04:39 BST 2020

Sau prin dmesg:

tom@rpi-yo3iti:~/c/drivere/Salzman/01 $ dmesg |grep Linux
[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 5.4.51-v7l+ (dom@buildbot) (gcc version 4.9.3 (crosstool-NG crosstool-ng-1.22.0-88-g8460611)) #1327 SMP Thu Jul 23 11:04:39 BST 2020
[    1.126787] usb usb1: Manufacturer: Linux 5.4.51-v7l+ xhci-hcd
[    1.128576] usb usb2: Manufacturer: Linux 5.4.51-v7l+ xhci-hcd
[   19.684585] mc: Linux media interface: v0.10
[   19.719580] videodev: Linux video capture interface: v2.00

Verificarea versiunii antetelor de kernel (kernel headers)

Fișierele antet (header) sunt necesare compilatorului pentru a verifica dacă o anumită funcție este corect utilizată într-un program. Această verificare se mai numește verificarea semnăturii unei funcții. Pentru verificarea semnăturii funcțiilor nu este necesar codul complet al implementării funcțiilor ci doar definiția lor. Ca atare, limitarea doar la includerea header-elor oferă o mare economie de cod și memorie.

În cazul concret al dezvoltării driverelor, header-ele kernelului trebuie să se potrivească cu versiunea de kernel:

tom@rpi-yo3iti:~/c/drivere/Salzman/01 $ apt search linux-header*
Sorting... Done
Full Text Search... Done
aufs-dkms/stable 4.19+20190211-1 all
  DKMS files to build and install aufs

linux-headers-4.18.0-3-common/stable 4.18.20-2+rpi1 all
  Common header files for Linux 4.18.0-3

linux-headers-4.18.0-3-common-rt/stable 4.18.20-2+rpi1 all
  Common header files for Linux 4.18.0-3-rt

linux-headers-4.9.0-6-all/stable 4.9.82-1+deb9u3+rpi2 armhf
  All header files for Linux 4.9 (meta-package)

linux-headers-4.9.0-6-all-armhf/stable 4.9.82-1+deb9u3+rpi2 armhf
  All header files for Linux 4.9 (meta-package)

[...]

... sper să nu avem probleme. :D Mai întâi trebuie instalate header-ele pentru kernel:

sudo rpi-update stable

Apoi:

apt get install raspberrypi-kernel-headers

Exemple generice, elemente de bază

Primul exemplu super simplu

În forma cea mai simplă (și care e complet inutilă) un driver are nevoie de cel puțin două funcții: una de inițializare și una de ieșire. În cazul programării driverelor, mesajele nu pot fi logate la consola normală ci doar în spațiul adresabil de kernel, cu ajutorul funcției printk.

#include <linux/module.h> /* necesar tuturor modulelor */
#include <linux/kernel.h> /* necesar KERN_INFO */

int init_module(void)
{
        printk(KERN_INFO "Hello world 1.\n");

        /*
         * O valoare diferită de zero înseamnă că funcția de inițializare a eșuat; modulul nu poate fi încărcat.
         */
        return 0;
}

void cleanup_module(void) 
{
        printk(KERN_INFO "Goodbye world 1.\n");
}

Un fișier Makefile simplu:

obj-m += hello-1.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Apoi:

tom@rpi-yo3iti:~/c/drivere/Salzman/01 $ make
make -C /lib/modules/5.4.51-v7l+/build M=/home/tom/c/drivere/Salzman/01 modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.51-v7l+'
  CC [M]  /home/tom/c/drivere/Salzman/01/hello-1.o
  Building modules, stage 2.
  MODPOST 1 modules
WARNING: modpost: missing MODULE_LICENSE() in /home/tom/c/drivere/Salzman/01/hello-1.o
see include/linux/module.h for more information
  CC [M]  /home/tom/c/drivere/Salzman/01/hello-1.mod.o
  LD [M]  /home/tom/c/drivere/Salzman/01/hello-1.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.4.51-v7l+'

Odată cu versiunea 2.6 de kernel este adoptată o nouă convenție pentru denumirea fișierelor: modulele kernel au extensia .ko în locul extensiei vechi .o pentru a fi diferențiate de fișierele-obiect clasice. Modulele kernel conțin o secțiune .modinfo suplimentară, unde este păstrată informația despre modul. Informația poate fi accesată cu comanda modinfo hello-*.ko:

tom@rpi-yo3iti:~/c/drivere/Salzman/01 $ modinfo hello-1.ko
filename:       /home/tom/c/drivere/Salzman/01/hello-1.ko
srcversion:     140276773A3090F6F33891F
depends:        
name:           hello_1
vermagic:       5.4.51-v7l+ SMP mod_unload modversions ARMv7 p2v8

Al doilea exemplu

Metoda acceptată (modernă) pentru definirea metodelor de inițializare (init) și ieșire (exit) este prezentată mai jos. Se poate alege orice denumire pentru aceste metode cu condiția de a menționa funcționalitatea prin module_init și module_exit ambele evidențiate în exemplul de mai jos:

/*
 * hello-2.c - demo pentru module_init() și module_exit()
 * Metoda mai nouă, preferată pentru init_module() și cleanup_module()
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

static int __init hello_2_init(void)
{
        printk(KERN_INFO "Hello, world 2\n");
        return 0;
}

static void __exit hello_2_exit(void)
{
        printk(KERN_INFO "Goodbye, world 2\n");
}

module_init(hello_2_init);
module_exit(hello_2_exit);

Acum avem două module kernel. Fișierul Makefile arată așa (pentru ambele module):

obj-m += hello-1.o
obj-m += hello-2.o

all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Compilarea:

Fișierele existente:

tom@rpi-yo3iti:~/c/drivere/Salzman/02 $ ls -lsa
total 20
4 drwxr-xr-x 2 tom  tom  4096 Jul 28 19:25 .
4 drwxr-xr-x 4 tom  tom  4096 Jul 28 19:16 ..
4 -rw-r--r-- 1 root root  335 Jul 28 19:25 hello-1.c
4 -rw-r--r-- 1 root root  448 Jul 28 19:23 hello-2.c
4 -rw-r--r-- 1 root root  176 Jul 28 19:25 Makefile

Compilarea se face cu comanda make. Se observă o eroare la liniile 8 și 10 (evidențiate) datorată lipsei directivei MODULE_LICENSE() prin care se specifică tipulde licențiere al codului:

tom@rpi-yo3iti:~/c/drivere/Salzman/02 $ make
make -C /lib/modules/5.4.51-v7l+/build M=/home/tom/c/drivere/Salzman/02 modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.51-v7l+'
  CC [M]  /home/tom/c/drivere/Salzman/02/hello-1.o
  CC [M]  /home/tom/c/drivere/Salzman/02/hello-2.o
  Building modules, stage 2.
  MODPOST 2 modules
WARNING: modpost: missing MODULE_LICENSE() in /home/tom/c/drivere/Salzman/02/hello-1.o
see include/linux/module.h for more information
WARNING: modpost: missing MODULE_LICENSE() in /home/tom/c/drivere/Salzman/02/hello-2.o
see include/linux/module.h for more information
  CC [M]  /home/tom/c/drivere/Salzman/02/hello-1.mod.o
  LD [M]  /home/tom/c/drivere/Salzman/02/hello-1.ko
  CC [M]  /home/tom/c/drivere/Salzman/02/hello-2.mod.o
  LD [M]  /home/tom/c/drivere/Salzman/02/hello-2.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.4.51-v7l+'

Al treilea exemplu - gestiunea automată a memoriei

/*  
 *  hello-3.c - Exemplu pentru comenzile-macro __init, __initdata și __exit.
 */
#include <linux/module.h>	/* Needed by all modules */
#include <linux/kernel.h>	/* Needed for KERN_INFO */
#include <linux/init.h>		/* Needed for the macros */

static int hello3_data __initdata = 3;

static int __init hello_3_init(void)
{
	printk(KERN_INFO "Hello, world %d\n", hello3_data);
	return 0;
}

static void __exit hello_3_exit(void)
{
	printk(KERN_INFO "Goodbye, world 3\n");
}

module_init(hello_3_init);
module_exit(hello_3_exit);

Comanda macro __init determină eliberarea memoriei alocată funcției init odată ce funcția de inițializare este completă, doar pentru driverele încorporate în kernel. Echivalentul pentru variabile este __initdata. Acest lucru nu este valabil pentru modulele (driverele) încărcate dinamic. La fel se comportă comanda macro __exit. Driverele incluse în kernel nu necesită funcții de eliberare a memoriei, în timp ce cele dinamice au nevoie de un mecanism de eliberare a memoriei. Aceste comenzi macro sunt definite în linux/init.h; în cazul meu, calea completă este:

/usr/src/linux-headers-5.4.51-v7+/include/linux/init.h

iată cum arată secțiunea coresponzătoare din init.h:

/* These are for everybody (although not all archs will actually
   discard it in modules) */
#define __init          __section(.init.text) __cold  __latent_entropy __noinitretpoline
#define __initdata      __section(.init.data)
#define __initconst     __section(.init.rodata)
#define __exitdata      __section(.exit.data)
#define __exit_call     __used __section(.exitcall.exit)

La boot, un mesaj similar cu: ...Freeing unused kernel memory: 236k freed... înseamnă exact eliberarea memoriei de de aceste module.

Al patrulea exemplu

În acest exemplu adăugăm comenzile maro care definesc tipul licenței, autorul codului, descrierea etc.

/*
 * hello-4.c - demo pentru documentarea modulelor
 *
 */

#include <linux/module.h> /* necesar tuturor modulelor */
#include <linux/kernel.h> /* necesar KERN_INFO */
#include <linux/init.h>   /* necesar pentru macro-uri */
#define AUTORUL_DRIVERULUI "Miron Iancu YO3ITI"
#define DRIVER_DESC "Un driver test, demo"

static int __init init_hello_4(void)
{
        printk(KERN_INFO "Hello, world 4.\n");
        return 0;
}

static void __exit cleanup_hello_4(void)
{
        printk(KERN_INFO "Goodbye, world 4.\n");
}

module_init(init_hello_4);
module_exit(cleanup_hello_4);

/*
 * Putem utiliza șiruri text, în felul următor:
 */

/*
 * Scăpăm de mesajele enervante legate de licență...
 */
MODULE_LICENSE("GPL");

/*
 * Sau cu ajutorul macro-urilor:
 */
MODULE_AUTHOR(AUTORUL_DRIVERULUI);      /* cine a scris acest modul ? */
MODULE_DESCRIPTION(DRIVER_DESC);        /* ce face acest modul ? */

/*
 * Acest modul folosește /dev/testdevice directiva macro MODULE_SUPPORTED_DEVICE
 * poate fi folosită (pe viitor) la configurarea automată a modulelor.
 * Pe moment nu este utilizată pentru altceva decât documentare.
 */
MODULE_SUPPORTED_DEVICE("testdevice");

Aceste informații pot fi afșare cu comanda modinfo: tom@rpi-yo3iti:~/c/drivere/02 $ modinfo hello-4.ko

filename:       /home/tom/c/drivere/Salzman/02/hello-4.ko
description:    Un driver test, demo
author:         Miron Iancu YO3ITI <miancuster at gmail dot com>
license:        GPL
srcversion:     C6368761702CBF9D1D1740D
depends:        
name:           hello_4
vermagic:       5.4.51-v7l+ SMP mod_unload modversions ARMv7 p2v8

Al cincilea exemplu

În codul de mai jos se exemplifică cum se pot schimba mesaje cu un modul kernel (driver) din spațiul utilizator, prin intermediul parametrilor introduși cu funcția module_param:

/*
 * hello-5.c - demo pentru interactivitate = cum se pot schimba mesaje prin parametri
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/stat.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Miron Iancu, YO3ITI, <miancuster at gmail dot com>");

static short int myshort = 1;
static int myint = 420;
static long mylong = 9999;
static char *mystring = "blah";
static int myintArray[2] = {-1, 1};
static int arr_argc = 0;

/*
 * module_param(foo, int, 0000)
 * primul parametru este denumirea parametrului
 * al doilea parametru este tipul
 * al treilea argument sunt biții pentru permisiuni,
 * pentru expunerea mai târziu a parametrilor în sysfs
 * (dacă sunt diferiți de zero
 */

module_param(myshort, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
MODULE_PARM_DESC(myshort, "Un număr întreg de dimensiuni mici");

module_param(myint, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
MODULE_PARM_DESC(myint, "Un număr întreg");

module_param(mylong, long, S_IRUSR);
MODULE_PARM_DESC(mylong, "Un număr întreg de dimensiuni mari");

module_param(mystring, charp, 0000); /* charp = char pointer */
MODULE_PARM_DESC(mystring, "Un șir de caractere");

/*
 * module_param_array(name, type, num, perm);
 * primul argument este denumirea parametrului (în acest caz denumirea șirului)
 * al doilea argument este tipul de dată al elementelor șirului
 * al treilea argument este un pointer către variabila care va stoca numărul
 * elementelor șirului inițializat de utilizat în momentul încărcării modulului în kernel
 * al patrulea argument sunt biții care stabilesc permisiunile
 */

module_param_array(myintArray, int, &arr_argc, 0000);
MODULE_PARM_DESC(myintArray, "Un șir de numere întregi");

static int __init hello_5_init(void)
{
        int i;
        printk(KERN_INFO "Hello, world 5\n===================\n");
        printk(KERN_INFO "myshort este un număr întreg de dimensiuni mici: %hd\n", myshort);
        printk(KERN_INFO "myint este un număr întreg: %d\n", myint);
        printk(KERN_INFO "mylong este un număr întreg de dimensiuni mari %ld\n", mylong);
        printk(KERN_INFO "mystring este un șir de caractere: %s\n", mystring);
        for (i = 0; i < (sizeof myintArray / sizeof(int)); i++)
        {
                printk(KERN_INFO "myintArray[%d] = %d\n", i, myintArray[i]);
        }
        printk(KERN_INFO "am primit %d argumente pentru myintArray.\n", arr_argc);
        return 0;
}

static void __exit hello_5_exit(void)
{
        printk(KERN_INFO "Goodbye, world 5\n");
}

module_init(hello_5_init);
module_exit(hello_5_exit);

Pentru adăugarea driver-ului se rulează comanda insmod cu parametri. Această metodă de inițializare este deosebit de utilă pentru a obține drivere care pot fi utilizate pentru o plajă largă de parametri de funcționare:

sudo insmod hello-5.ko mystring="un_exemplu_de_șir" myint=45 myshort=3 mylong=765765 myintArray=-1,2

Log-urile de kernel pot fi consultate cu dmesg

[13561.437899] myshort este un număr întreg de dimensiuni mici: 3
[13561.437938] myint este un număr întreg: 45
[13561.437948] mylong este un număr întreg de dimensiuni mari 765765
[13561.437958] mystring este un șir de caractere: un_exemplu_de_șir
[13561.437968] myintArray[0] = -1
[13561.437978] myintArray[1] = 2
[13561.437988] am primit 2 argumente pentru myintArray.

Exemple pe tipuri de drivere

Drivere de tip char

TODO

Drivere de sistem

TODO

Link-uri externe