In questo articolo sviluppiamo da zero un semplice driver per il Kernel Linux passo dopo passo, cercando di mettere in evidenza gli aspetti più interessanti. Scrivere un modulo in kernel space ci permette di andare a toccare con mano l'hardware, cosa assolutamente proibita in user space. Andiamo a sviluppare un driver per architettura PC e, tutto quello che diremo vale anche se si vuole cross-compilare il driver per la propria Rasperry-Pi o board custom.
Linux permette di sviluppare driver sia linkati staticamente al kernel, sia come moduli separati da caricare durante il funzionamento del sistema. L'approccio nel loro sviluppo è sostanzialmente identico. In questo articolo creiamo un modulo per il kernel per comodità di sviluppo e debug.
Preparazione
Un modulo non può essere compilato da solo come un normale programma in C. Necessita infatti di avere almeno i kernel headers del kernel verso cui lo andremo a linkare dinamicamente. Un modulo così fatto potrà essere installato solo su quel particolare kernel (versione, configurazione,...) e non sarà compatibile con altri kernel.
Su Debian o Ubuntu è necessario installare i seguenti pacchetti: module-assistant e linux-headers-XXX
sudo apt-get install module-assistant
per i kernel header occorre sapere quale kernel attualmente è in esecuzione con il comando
uname -a
Ad esempio se l'output sarà "Linux ubuntu 3.8.0-29-generic " dobbiamo installare il pacchetto linux-headers-3.8.0-29-generic
Dobbiamo ovviamente avere installato il compilatore gcc e make
Creiamo l'Hello World dei driver
Innanzitutto dobbiamo creare un Makefile
ifneq ($(KERNELRELEASE),)
# call from kernel build system
pippo-objs := pippo_drv.o
obj-m := pippo.o
else
EXTRA_CFLAGS += -I$(KERNELDIR)/include
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all: clean modules modules:$(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD)/../include modules
endif clean:rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
ATTENZIONE: Tutti gli spazi ad inizio linea sono TABULAZIONI. Il make pretende questo carattere, altrimenti riceverete errori. Se fate copia-incolla del codice, fate attenzione a fare le sostituzioni a mano
In grassetto ho evidenziato le parti interessanti, che vanno cambiate per ogni driver, il resto del Makefile non necessita di modifche.
pippo-objs è una variabile che include l'elenco dei file oggetto da cui sarà composto il nostro driver, andando a generare un singolo file oggetto di nome pippo.o. In automatico saranno compilati tutti i .c corrispondenti all'elenco dei .o . Nel nostro caso abbiamo un unico file sorgente di nome pippo_drv.c
obj-m è l'elenco dei moduli del kernel da compilare. In questo caso il nostro pippo.o generato grazie alla linea precedente diventerà pippo.ko
KERNEL_DIR indica il path del kernel a cui ci andiamo a linkare. In caso di compilazione nativa ho riportato il path generato tramite uname. Se stessimo compilando per un altro kernel basterà indicare il path assoluto verso il top dei sorgenti del kernel (già compilato e configurato)
Ora creiamo il nostro primo driver pippo_drv.c
Alcuni Header necessari:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
Init function, questa funzione viene chiamata quando andiamo ad installare il modulo
static int __init pippo_init(void)
{
printk("Ciao da PIPPO!\n");
return 0;
}
__init indica al kernel che questa funzione non è più utilizzata dopo l'inizializzazione del modulo e pertanto può venire deallocata
Exit function, chiamta quando facciamo rmmod del driver
static void __exit pippo_exit(void)
{
printk("Bye Bye da PIPPO!\n");
}
Usiamo due macro per indicare al kernel quali sono le init e le exit function
module_init(pippo_init);
module_exit(pippo_exit);
Inseriamo ora alcuni metadati. In particolare la licenza è bene sia Free, onde evitare che il kernel vada a disabilitare tutto il codice che non può essere linkato con moduli non-free per questioni di licenza.
MODULE_LICENSE("GPL");
MODULE_AUTHOR ("Giorgio Delmondo <giorgio AT lvdsystems.it>");
MODULE_DESCRIPTION("PIPPO Driver");
MODULE_VERSION("1.0.0");
Ora siamo pronti per compilare e testare il nostro primo driver! Diamo il comando:
make
ed a questo punto ci troviamo con un file pippo.ko in output.
Per installare il driver diamo il comando:
sudo insmod pippo.ko
e per scaricarlo:
sudo rmmod pippo.ko
Dove sono finite le nostre printk? Le vediamo dando il comando dmesg:
dmesg
[ 6155.325170] Ciao da PIPPO!
[ 6163.979046] Bye Bye da PIPPO!
Comunicare tra kernel space e user space
Una volta creato il nostro primo scheletro di driver, vogliamo fare in modo di poter comunicare da un applicativo user space verso il nostro driver (ed infine finalmente con l'hardware). Per fare questo dobbiamo create un device file (/dev/pippo).
Che cos'è un device? Un device è un file speciale, può essere di tipo char o block. I block device vengono utilizzati per la gestione di dispositivi di storage (i dischi), mentre i char vengono utilizzati per tutti gli altri tipi di device e permettono di [...]
ATTENZIONE: quello che hai appena letto è solo un estratto, l'Articolo Tecnico completo è composto da ben 2596 parole ed è riservato agli ABBONATI. Con l'Abbonamento avrai anche accesso a tutti gli altri Articoli Tecnici che potrai leggere in formato PDF per un anno. ABBONATI ORA, è semplice e sicuro.
Eccezionale, sono molto colpito, mi piacerebbe molto imparare a realizzare dei driver per le mie periferiche… Sarei molto felice se i tuoi articoli sull’argomento continuassero. Nel frattempo ti faccio i miei complimenti.
Salve, complimenti per la chiarezza dell’articolo . Spero in un successivo articolo per approfondire i punti rimasti sospesi.
Grazie 🙂
Spero di riuscire a completare presto il secondo articolo
Buonasera, l’articolo è molto interessante e apre la strada a moltissimi altri.
Dal mio punto di vista, sarebbe interessante approfondire l’accesso all’hw e la gestione degli interrupt.
Ottimo articolo.
Aspetto il prossimo, con esempi pratici.
Grazie
Grazie per l’articolo! Attendiamo il prossimo!!!
Complimenti rimango in attesa dei nuovi articoli sull’argomento.
Se possibile con la realizzazione di driver a partire dai datasheets dei vari componenti presenti sulle periferiche hardware di I/O.
Grazie per il tuo impegno
Come i commenti descritti dagli Abbonati, non vedo l’ora di altri articoli inerenti al I/O con porte es USB e via dicendo grazie
wow semplicemente super-interessante… attendo con inpazienza gli altri articoli
un feedback positivo anche da me. E’ raro leggere in italiano documentazione di questo livello.