Opis
Rozpocznijmy dyskusję z sterownikiem postaci w Linux. Kernel podziela kierowców na trzy kategorie:
Sterowniki postaci - To są sterowniki, które nie mają zbyt dużej ilości danych. Kilka przykładów sterowników znaków to sterownik ekranu dotykowego, sterownik UART itp. Wszystkie są sterownikami znaków, ponieważ przesyłanie danych odbywa się przez charakter według charakteru.
Blokuj sterowniki - Są to sterowniki, które zajmują się zbyt dużą ilością danych. Transfer danych odbywa się blok według bloku, ponieważ zbyt wiele danych musi zostać przeniesione. Przykładem sterowników blokowych są SATA, NVME itp.
Sterowniki sieciowe - Są to sterowniki, które funkcjonują w sieciowej grupie sterowników. Tutaj przesyłanie danych odbywa się w postaci pakietów danych. Kierowcy bezprzewodowe, takie jak Atheros w tej kategorii.
W tej dyskusji skupimy się tylko na sterowniku postaci.
Na przykład podejmiemy proste operacje odczytu/zapisu, aby zrozumieć podstawowy sterownik znaku. Zasadniczo każdy sterownik urządzenia ma te dwie minimalne operacje. Dodatkowa operacja może być otwarta, blisko, IOCTL itp. W naszym przykładzie nasz sterownik ma pamięć w przestrzeni jądra. Ta pamięć jest przydzielana przez sterownik urządzenia i może być uważana za pamięć urządzenia, ponieważ nie wiąże się to z komponentem sprzętowym. Sterownik tworzy interfejs urządzenia w katalogu /dev, który może być używany przez programy przestrzeni użytkowników w celu uzyskania dostępu do sterownika i wykonywania operacji obsługiwanych przez sterownik. W przypadku programu przestrzeni użytkowników te operacje są jak każde inne operacje plików. Program przestrzeni użytkowników musi otworzyć plik urządzenia, aby uzyskać instancję urządzenia. Jeśli użytkownik chce wykonać operację odczytu, do tego można użyć wywołania systemu odczytu. Podobnie, jeśli użytkownik chce wykonać operację zapisu, wywołanie systemu zapisu można użyć do osiągnięcia operacji zapisu.
Sterownik postaci
Rozważmy wdrożenie sterownika znaku z operacjami danych odczytu/zapisu.
Zaczynamy od przyjmowania instancji danych urządzenia. W naszym przypadku jest to „struct cdrv_device_data”.
Jeśli widzimy pola tej struktury, mamy CDEV, bufor urządzenia, rozmiar bufora, instancję klasową i obiekt urządzenia. Są to minimalne pola, w których powinniśmy zaimplementować sterownik znaku. Zależy to od implementarza, od których dodatkowe pola chce dodać, aby poprawić funkcjonowanie sterownika. Tutaj staramy się osiągnąć minimalne funkcjonowanie.
Następnie powinniśmy utworzyć obiekt struktury danych urządzenia. Używamy instrukcji, aby przydzielić pamięć w sposób statyczny.
struct cdrv_device_data char_device [cdrv_max_minors];
Pamięć tę można również podzielić dynamicznie za pomocą „Kmalloc”. Zachowajmy implementację tak prostą, jak to możliwe.
Powinniśmy przyjąć implementację funkcji odczytu i zapisu. Prototyp tych dwóch funkcji jest zdefiniowany przez strukturę sterownika urządzenia Linux. Implementacja tych funkcji musi być zdefiniowana przez użytkownika. W naszym przypadku rozważyliśmy następujące:
Przeczytaj: Operacja, aby uzyskać dane z pamięci sterownika do przestrzeni użytkownika.
static ssize_t cdrv_read (plik struct *plik, char __User *user_buffer, rozmiar size_t, loff_t *offset);
Napisz: Operacja przechowywania danych do pamięci sterownika z przestrzeni użytkownika.
static ssize_t cdrv_write (plik struct *, const char __User *user_buffer, rozmiar size_t, loff_t *offset);
Zarówno operacje, odczyt i zapisz, muszą być zarejestrowane jako część struct file_operations cdrv_fops. Są one zarejestrowane w ramach sterownika urządzenia Linux w init_cdrv () sterownika. Wewnątrz funkcji init_cdrv () wszystkie zadania konfiguracyjne są wykonywane. Niewiele zadań jest następujące:
Pełny przykładowy kod podstawowego sterownika urządzenia znakowego jest następujący:
#włączaćTworzymy przykładowy makefile, aby skompilować podstawowy sterownik znaku i aplikację testową. Nasz kod sterownika jest obecny w CRDV.C i kod aplikacji testowej jest obecny w CDRV_APP.C.
obj-m+= cdrv.oPo wydaniu Makefile powinniśmy uzyskać następujące dzienniki. Dostajemy również CDRV.KO i wykonywalne (CDRV_App) dla naszej aplikacji testowej:
root@haxv-srathore-2:/home/cinauser/kernel_articles# MakeOto przykładowy kod dla aplikacji testowej. Ten kod implementuje aplikację testową, która otwiera plik urządzenia utworzony przez sterownik CDRV i zapisuje do niego „dane testowe”. Następnie odczytuje dane z sterownika i drukuje je po odczytaniu danych do wydrukowania jako „dane testowe”.
#włączaćGdy weźmiemy wszystkie rzeczy, możemy użyć następującego polecenia, aby wstawić podstawowy sterownik znaku do jądra Linux:
root@haxv-sathore-2:/home/cinauser/kernel_articles# insmod cdrv.KoPo włożeniu modułu otrzymujemy następujące wiadomości z DMESG i tworzymy plik urządzenia utworzony w /Dev as /dev /cdrv_dev:
root@haxv-sathore-2:/home/cinauser/kernel_articles# dmesgTeraz wykonaj aplikację testową z następującym poleceniem w powładzie Linux. Ostateczna wiadomość drukuje dane odczytu z sterownika, które są dokładnie takie samo, jak to, co napisaliśmy podczas operacji zapisu:
root@haxv-sathore-2:/home/cinauser/kernel_articles# ./cdrv_appMamy kilka dodatkowych wydruków na ścieżce zapisu i odczytu, które można zobaczyć za pomocą polecenia DMESG. Kiedy wydawamy polecenie DMESG, otrzymujemy następujące dane wyjściowe:
root@haxv-sathore-2:/home/cinauser/kernel_articles# dmesgWniosek
Przeszliśmy przez podstawowy sterownik znaków, który implementuje podstawowe operacje zapisu i odczytu. Omówiliśmy również przykładowy makefile, aby skompilować moduł wraz z aplikacją testową. Aplikacja testowa została napisana i omówiona w celu wykonania operacji zapisu i odczytu z miejsca użytkownika. Wykazaliśmy również kompilację i wykonanie modułu i aplikacji testowej z dziennikami. Aplikacja testowa zapisuje kilka bajtów danych testowych, a następnie odczytuje je z powrotem. Użytkownik może porównać oba dane, aby potwierdzić prawidłowe funkcjonowanie sterownika i aplikacji testowej.