sobota, 3 sierpnia 2013

Programowanie sterowników pod Linux cz.1 - wprowadzenie

Zaczynamy. Na początek seria wpisów dotycząca programowania sterowników pod Linux. Poza wpisami wszystkim zainteresowanym proponuję literaturę którą sam przerabiam:


Aby programować sterowniki pod Linuxa trzeba znać język C przynajmniej na podstawowym poziomie (w innym przypadku polecam http://cm.bell-labs.com/cm/cs/cbook/). Zawsze trzeba od czegoś zacząć, my zaczniemy od ściągnięcia aktualnych źródeł linuxowego kernela dostępnych tutaj: https://www.kernel.org/. Ściągamy najnowszą stabilną wersję.


Po co nam źródła kernela?

  • Aby programować sterowniki musimy nieco zagłębić się w działanie systemu Linux.
  • W katalogu /drivers/ mamy doskonałe przykłady sterowników

Przystąpmy więc do omówienia samego programowania sterownika. Sterownik to warstwa pośrednicząca pomiędzy urządzeniem a systemem operacyjnym. Aby zapewnić uniwersalność kursu, będziemy pisać kod dla wirtualnego urządzenia, za które posłuży nam 100-elementowa tablica znakowa.

W systemie Linux mamy do czynienia z trzema typami sterowników:

  • znakowe (mysz, klawiatura)
  • blokowe (dysk twardy, pendrive)
  • sieciowe (moduł wifi)

Nasz sterownik będzie sterownikiem znakowym, ponieważ zajmiemy się jedynie kwestiami związanymi z transmisją danych z/do urządzenia. Sterownik ma za zadanie porozumiewać się z urządzeniem, konkretnie podejmować operacje związane z:

  • podłączeniem urządzenia (czynności inicjalizacyne, alokacja potrzebnych zasobów)
  • odłączeniem urządzenia(dealokacja zasobów) 
  • zapisywaniem danych do urządzenia
  • odczytywaniem danych z urządzenia

Są to cztery podstawowe operacje za które odpowiadją funkcje open(), release(), write() oraz read(), a do ich realizacji w zupełności wystarczy tablica charów. Nasze urządzenie definiujemy więc w następujący sposób:

struct device{
   char array[100];
};

Warto także wspomnieć o pewnych różnicach w programowaniu kernela. Najważniejsza zasada - nie korzystamy ze standardowych bibliotek języka C, ani żadnych innych bibliotek, które nie są udostępniane bezpośrednio przez kernel (z małymi wyjątkami). Pliki nagłówkowe, które będą nam potrzebne na początek to:

  • linux/kernel.h 
  • linux/module.h 
  • linux/fs.h // operacje na urządzeniu
  • linux/uaccess.h // copy_to_user(), copy_from_user()
  • linux/cdev.h // biblioteka dla sterownika znakowego

Moduły kernela muszą zawierać dwa podstawowe makra:

  • module_init()
  • module_exit()

Jako argument module_init()  podajemy nazwę funkcji która ma zostać odpalona przy inicjalizacji modułu, natomiast dla module_exit() funkcję wykonywaną w momencie jego usunięcia.

Wykorzystamy także dwa makra:

  • __init - dla funkcji wykorzystywanej jedynie przy inicjalizacji
  • __exit - dla funkcji wykorzystywanej jedynie przy deaktywacji

Zbierzmy to w całość:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccsess.h>
#include <linux/cdev.h>
struct virtual_device{
   char array[100]; 
};

static int __init load(void)

   printk("Aktywacja sterownika\n");


static void __exit unload(void) 

   printk("Deaktywacja sterownika\n"); 


module_init(load);
module_exit(unload);

Funkcja printk() jest odpowiednikiem printf() w kernelu, w działaniu jest bardzo podobna. Funkcje load() i unload() przekazujemy do module_init() oraz module_exit().

Nadszedł czas aby wypróbować nasz moduł. W katalogu z kodem modułu stwórzmy plik Makefile zakładając, że kod modułu znajduje się w pliku driver.c (w przypadku innej nazwy należy zmodyfikować poniższy kod).

obj-m := driver.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

Następnie za pomocą polecenia make kompilujemy nasz moduł. Jeśli wszystko przebiegło pomyślnie w katalogu zostało dodane kilka plików, między innymi plik driver.ko, który jest naszym modułem. Mając gotowy moduł korzystamy z poleceń:

insmod driver.ko - ładujemy nasz moduł
lsmod - wyświetlamy wszystkie aktywne moduły (powinien być widoczny moduł driver)
dmesg - wyświetlamy bufor warstwy kernela
rmmod driver.ko - usuwamy moduł
dmesg - ponownie wyświetlamy bufor warstwy kernelaj

Jeśli wszystko przebiegło pomyślnie w terminalu powinny być widoczne komunikaty:


Brak komentarzy:

Prześlij komentarz