Linux Device Driver: sviluppo di un modulo base

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 e potrai fare il download in formato PDF eBook e Mobi per un anno. ABBONATI ORA, è semplice e sicuro.

Scarica subito una copia gratis

10 Commenti

  1. Mariano Neroni mneroni 22 Giugno 2015
  2. Alessandro.Bonvicini 25 Giugno 2015
  3. MarciTy 29 Giugno 2015
  4. Francesco Francesco 30 Giugno 2015
  5. roberto.garavaglia 6 Luglio 2015
  6. Massimo.Cala' 8 Luglio 2015
  7. hazzino 15 Ottobre 2015
  8. Andrea Martin Andrea Martin 16 Febbraio 2016
  9. giancaos1 13 Agosto 2016

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend