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:
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>struct virtual_device{
{
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