Aby zwiększyć niezawodność i stabilność modułów dostawcy, postępuj zgodnie z tymi wskazówkami. Wiele wskazówek, jeśli są przestrzegane, może ułatwić określenie prawidłowej kolejności ładowania modułów i kolejności, w jakiej sterowniki muszą sprawdzać urządzenia.
Moduł może być biblioteką lub sterownikiem.
Moduły bibliotek to biblioteki, które udostępniają interfejsy API do używania przez inne moduły. Takie moduły zwykle nie są przeznaczone do konkretnego sprzętu. Przykłady modułów biblioteki to moduł szyfrowania AES, platforma
remoteprocskompilowana jako moduł i moduł logbuffer. Kod modułu wmodule_init()jest uruchamiany w celu skonfigurowania struktur danych, ale żaden inny kod nie jest uruchamiany, chyba że zostanie wywołany przez moduł zewnętrzny.Moduły sterowników to sterowniki, które sprawdzają lub wiążą się z określonym typem urządzenia. Takie moduły są przeznaczone dla konkretnych urządzeń. Przykłady modułów sterownika to UART, PCIe i sprzęt enkodera wideo. Moduły sterownika są aktywowane tylko wtedy, gdy powiązane z nimi urządzenie jest obecne w systemie.
Jeśli urządzenia nie ma, uruchamia się tylko kod modułu
module_init(), który rejestruje sterownik w ramach podstawowego sterownika.Jeśli urządzenie jest obecne, a sterownik pomyślnie je wykryje lub się z nim połączy, może zostać uruchomiony kod innego modułu.
Prawidłowe używanie funkcji inicjowania i zamykania modułu
Moduły sterownika muszą rejestrować sterownik w module_init() i wyrejestrowywać go w module_exit(). Jednym ze sposobów egzekwowania tych ograniczeń jest używanie makr otoki, które pozwalają uniknąć bezpośredniego użycia makr module_init(), *_initcall() lub module_exit().
W przypadku modułów, które można odłączyć, użyj
module_subsystem_driver(). Przykłady:module_platform_driver(),module_i2c_driver()imodule_pci_driver().W przypadku modułów, których nie można odinstalować, użyj
builtin_subsystem_driver()Przykłady:builtin_platform_driver(),builtin_i2c_driver()ibuiltin_pci_driver().
builtin_subsystem_driver()
Niektóre moduły sterownika używają symboli module_init() i module_exit(), ponieważ rejestrują więcej niż 1 sterownik. W przypadku modułu sterownika, który używa module_init() i module_exit() do rejestrowania wielu sterowników, spróbuj połączyć sterowniki w jeden sterownik. Możesz na przykład rozróżniać urządzenia za pomocą compatibleciągu znaków lub danych dodatkowych urządzenia zamiast rejestrować osobne sterowniki.
Możesz też podzielić moduł sterownika na 2 moduły.
Wyjątki funkcji inicjującej i zamykającej
Moduły biblioteki nie rejestrują sterowników i są zwolnione z ograniczeń dotyczących funkcji module_init() i module_exit(), ponieważ mogą ich potrzebować do konfigurowania struktur danych, kolejek zadań lub wątków jądra.
Używanie makra MODULE_DEVICE_TABLE
Moduły sterownika muszą zawierać makro MODULE_DEVICE_TABLE, które umożliwia przestrzeni użytkownika określenie urządzeń obsługiwanych przez moduł sterownika przed załadowaniem modułu. Android może używać tych danych do optymalizacji ładowania modułów, np. aby uniknąć ładowania modułów na urządzeniach, które nie są obecne w systemie. Przykłady użycia makra znajdziesz w kodzie źródłowym.
Unikanie niezgodności CRC z powodu typów danych zadeklarowanych z wyprzedzeniem
Nie dodawaj plików nagłówkowych, aby uzyskać wgląd w typy danych zadeklarowane z wyprzedzeniem.
Niektóre struktury, unie i inne typy danych zdefiniowane w pliku nagłówkowym (header-A.h) można zadeklarować w innym pliku nagłówkowym (header-B.h), który zwykle używa wskaźników do tych typów danych. Ten wzorzec kodu oznacza, że jądro celowo próbuje zachować strukturę danych jako prywatną dla użytkowników header-B.h.
Użytkownicy header-B.h nie powinni używać header-A.h, aby bezpośrednio uzyskiwać dostęp do wewnętrznych elementów tych struktur danych z deklaracją wyprzedzającą. Powoduje to problemy z CONFIG_MODVERSIONS CRC
mismatch (które generują problemy ze zgodnością z ABI), gdy inny kernel
(np. kernel GKI) próbuje załadować moduł.
Na przykład struktura struct fwnode_handle jest zdefiniowana w pliku include/linux/fwnode.h, ale jest zadeklarowana w pliku include/linux/device.h jako struct fwnode_handle;, ponieważ jądro próbuje ukryć szczegóły struktury struct fwnode_handle przed użytkownikami pliku include/linux/device.h. W tej sytuacji nie dodawaj #include <linux/fwnode.h> w module, aby uzyskać dostęp do użytkowników struct fwnode_handle. Każdy projekt, w którym musisz uwzględnić takie pliki nagłówkowe, wskazuje na zły wzorzec projektowy.
Nie uzyskuj bezpośredniego dostępu do struktur jądra
Bezpośredni dostęp do podstawowych struktur danych jądra lub ich modyfikowanie może prowadzić do niepożądanych zachowań, w tym wycieków pamięci, awarii i braku zgodności z przyszłymi wersjami jądra. Struktura danych jest podstawową strukturą danych jądra, jeśli spełnia którykolwiek z tych warunków:
Struktura danych jest zdefiniowana w sekcji
KERNEL-DIR/include/. Na przykład:struct deviceistruct dev_links_info. Struktury danych zdefiniowane winclude/linux/socsą wyłączone.Struktura danych jest przydzielana lub inicjowana przez moduł, ale jest udostępniana jądru przez przekazanie jej pośrednio (za pomocą wskaźnika w strukturze) lub bezpośrednio jako danych wejściowych w funkcji wyeksportowanej przez jądro. Na przykład moduł sterownika
cpufreqinicjujestruct cpufreq_driver, a następnie przekazuje go jako dane wejściowe docpufreq_register_driver(). Od tego momentu moduł sterownikacpufreqnie powinien modyfikowaćstruct cpufreq_driverbezpośrednio, ponieważ wywołaniecpufreq_register_driver()sprawia, żestruct cpufreq_driverjest widoczny dla jądra.Struktura danych nie została zainicjowana przez moduł. Na przykład
struct regulator_devzwrócone przezregulator_register().
Dostęp do podstawowych struktur danych jądra jest możliwy tylko za pomocą funkcji eksportowanych przez jądro lub parametrów wyraźnie przekazywanych jako dane wejściowe do funkcji dostawcy. Jeśli nie masz interfejsu API ani haka dostawcy, aby modyfikować części podstawowej struktury danych jądra, prawdopodobnie jest to celowe i nie należy modyfikować struktury danych z modułów. Nie modyfikuj na przykład żadnych pól w struct device ani
struct device.links.
Aby zmodyfikować
device.devres_head, użyj funkcjidevm_*(), np.devm_clk_get(),devm_regulator_get()lubdevm_kzalloc().Aby zmodyfikować pola w
struct device.links, użyj interfejsu API linku do urządzenia, np.device_link_add()lubdevice_link_del().
Nie analizuj węzłów devicetree z właściwością compatible
Jeśli węzeł drzewa urządzeń (DT) ma właściwość compatible, automatycznie lub po wywołaniu funkcji of_platform_populate() w nadrzędnym węźle DT (zwykle przez sterownik urządzenia nadrzędnego) przydzielany jest dla niego węzeł struct device. Domyślnym założeniem (z wyjątkiem niektórych urządzeń zainicjowanych wcześniej na potrzeby harmonogramu) jest to, że węzeł DT z właściwością compatible ma właściwość struct device i pasujący sterownik urządzenia. Wszystkie inne wyjątki są już obsługiwane przez kod wyższego poziomu.
Dodatkowo fw_devlink (wcześniej of_devlink) uznaje węzły DT z właściwością compatible za urządzenia z przydzielonym struct device, które są sprawdzane przez sterownik. Jeśli węzeł DT ma właściwość compatible, ale przydzielony struct device nie jest sprawdzany, fw_devlink może blokować sprawdzanie przez urządzenia konsumenckie lub blokować wywoływanie funkcji sync_state() na urządzeniach dostawcy.
Jeśli sterownik używa funkcji of_find_*() (np. of_find_node_by_name() lub of_find_compatible_node()) do bezpośredniego wyszukiwania węzła DT, który ma właściwość compatible, a następnie analizuje ten węzeł DT, napraw moduł, pisząc sterownik urządzenia, który może sondować urządzenie, lub usuń właściwość compatible (możliwe tylko wtedy, gdy nie została ona jeszcze przesłana do upstream). Aby omówić alternatywne rozwiązania, skontaktuj się z zespołem Android Kernel pod adresem kernel-team@android.com i przygotuj się na uzasadnienie swoich przypadków użycia.
Wyszukiwanie dostawców za pomocą uchwytów DT
W miarę możliwości odwołuj się do dostawcy za pomocą uchwytu (odwołania lub wskaźnika do węzła DT) w DT. Używanie standardowych powiązań DT i uchwytów do odwoływania się do dostawców umożliwia fw_devlink (wcześniej of_devlink) automatyczne określanie zależności między urządzeniami przez analizowanie DT w czasie działania. Jądro może wtedy automatycznie sprawdzać urządzenia w odpowiedniej kolejności, co eliminuje potrzebę zamawiania modułów lub MODULE_SOFTDEP().
Starszy scenariusz (brak obsługi DT w jądrze ARM)
Wcześniej, zanim do jąder ARM dodano obsługę DT, urządzenia takie jak urządzenia dotykowe wyszukiwały dostawców, np. regulatory, za pomocą globalnie unikalnych ciągów znaków.
Na przykład sterownik ACME PMIC może rejestrować lub reklamować wiele regulatorów (np. acme-pmic-ldo1–acme-pmic-ldo10), a sterownik dotykowy może wyszukiwać regulator za pomocą regulator_get(dev, "acme-pmic-ldo10").
Na innej płytce LDO8 może jednak zasilać urządzenie dotykowe, co tworzy skomplikowany system, w którym ten sam sterownik dotykowy musi określać prawidłowy ciąg wyszukiwania dla regulatora na każdej płytce, na której jest używane urządzenie dotykowe.
Obecny scenariusz (obsługa DT w jądrze ARM)
Po dodaniu obsługi DT do jąder ARM konsumenci mogą identyfikować dostawców w DT, odwołując się do węzła drzewa urządzenia dostawcy za pomocą phandle.
Klienci mogą też nazywać zasoby na podstawie ich zastosowania, a nie dostawcy. Na przykład sterownik dotykowy z poprzedniego przykładu może używać funkcji regulator_get(dev, "core") i regulator_get(dev, "sensor"), aby uzyskać informacje o dostawcach, którzy zasilają rdzeń i czujnik urządzenia dotykowego. Powiązany opis urządzenia dla takiego urządzenia jest podobny do tego przykładowego kodu:
touch-device {
compatible = "fizz,touch";
...
core-supply = <&acme_pmic_ldo4>;
sensor-supply = <&acme_pmic_ldo10>;
};
acme-pmic {
compatible = "acme,super-pmic";
...
acme_pmic_ldo4: ldo4 {
...
};
...
acme_pmic_ldo10: ldo10 {
...
};
};
Scenariusz najgorszy z możliwych
Niektóre sterowniki przeniesione ze starszych jąder zawierają w DT starsze zachowania, które wykorzystują najgorszą część starszego schematu i wymuszają ją na nowszym schemacie, który ma ułatwiać pracę. W takich sterownikach sterownik klienta odczytuje ciąg znaków, który ma być używany do wyszukiwania, za pomocą właściwości DT specyficznej dla urządzenia, dostawca używa innej właściwości specyficznej dla dostawcy, aby zdefiniować nazwę, która ma być używana do rejestrowania zasobu dostawcy, a następnie klient i dostawca nadal używają tego samego starego schematu używania ciągów znaków do wyszukiwania dostawcy. W tym najgorszym z możliwych scenariuszy:
Sterownik dotykowy używa kodu podobnego do tego:
str = of_property_read(np, "fizz,core-regulator"); core_reg = regulator_get(dev, str); str = of_property_read(np, "fizz,sensor-regulator"); sensor_reg = regulator_get(dev, str);DT używa kodu podobnego do tego:
touch-device { compatible = "fizz,touch"; ... fizz,core-regulator = "acme-pmic-ldo4"; fizz,sensor-regulator = "acme-pmic-ldo4"; }; acme-pmic { compatible = "acme,super-pmic"; ... ldo4 { regulator-name = "acme-pmic-ldo4" ... }; ... acme_pmic_ldo10: ldo10 { ... regulator-name = "acme-pmic-ldo10" }; };
Nie modyfikuj błędów interfejsu API platformy
Interfejsy API platformy, takie jak regulator, clocks, irq, gpio, phys i extcon, zwracają wartość błędu -EPROBE_DEFER, aby wskazać, że urządzenie próbuje przeprowadzić test, ale w tej chwili nie może tego zrobić, a jądro powinno ponowić próbę później. Aby mieć pewność, że w takich przypadkach funkcja .probe() na urządzeniu zawiedzie zgodnie z oczekiwaniami, nie zastępuj ani nie zmieniaj mapowania wartości błędu.
Zastąpienie lub ponowne mapowanie wartości błędu może spowodować usunięcie znaku -EPROBE_DEFER i sprawić, że urządzenie nigdy nie zostanie sprawdzone.
Korzystanie z wariantów interfejsu API devm_*()
Gdy urządzenie uzyska dostęp do zasobu za pomocą interfejsu devm_*() API, zasób jest automatycznie zwalniany przez jądro, jeśli urządzenie nie może go wykryć lub wykryje go, ale później zostanie odłączone. Ta funkcja sprawia, że kod obsługi błędów w funkcji probe() jest bardziej przejrzysty, ponieważ nie wymaga skoków goto w celu zwolnienia zasobów uzyskanych przez devm_*(), i upraszcza operacje odłączania sterownika.
Obsługa usuwania powiązania sterownika urządzenia
Rozważnie odłączaj sterowniki urządzeń i nie pozostawiaj odłączenia niezdefiniowanego, ponieważ niezdefiniowane nie oznacza niedozwolonego. Musisz w pełni zaimplementować usuwanie powiązania sterownika urządzenia lub wyraźnie wyłączyć usuwanie powiązania sterownika urządzenia.
Wdrażanie usuwania powiązania sterownika urządzenia
Jeśli zdecydujesz się na pełne wdrożenie usuwania powiązania sterowników urządzeń, usuń powiązanie sterowników urządzeń w sposób prawidłowy, aby uniknąć wycieków pamięci lub zasobów oraz problemów z bezpieczeństwem. Możesz powiązać urządzenie ze sterownikiem, wywołując funkcję probe() sterownika, a także odłączyć urządzenie, wywołując funkcję remove() sterownika. Jeśli nie ma funkcji remove(), jądro może nadal odłączyć urządzenie. Rdzeń sterownika zakłada, że sterownik nie musi wykonywać żadnych czynności czyszczących po odłączeniu od urządzenia. Sterownik odłączony od urządzenia nie musi wykonywać żadnych czynności związanych z czyszczeniem, jeśli spełnione są oba te warunki:
Wszystkie zasoby pozyskiwane przez funkcję
probe()sterownika są pozyskiwane za pomocą interfejsówdevm_*()API.Urządzenie nie wymaga sekwencji wyłączania ani wyciszania.
W takiej sytuacji podstawowy sterownik zwalnia wszystkie zasoby uzyskane za pomocą interfejsów API devm_*(). Jeśli którekolwiek z powyższych stwierdzeń jest nieprawdziwe, sterownik musi przeprowadzić czyszczenie (zwolnić zasoby i zamknąć lub wyciszyć sprzęt), gdy odłączy się od urządzenia. Aby mieć pewność, że urządzenie może prawidłowo odłączyć moduł sterownika, użyj jednej z tych opcji:
Jeśli sprzęt nie wymaga sekwencji wyłączania lub wyciszania, zmień moduł urządzenia, aby pozyskiwać zasoby za pomocą interfejsów API
devm_*().Zaimplementuj operację sterownika
remove()w tej samej strukturze co funkcjaprobe(), a następnie wykonaj czynności czyszczenia za pomocą funkcjiremove().
Jawne wyłączenie odłączania sterownika urządzenia (niezalecane)
Jeśli zdecydujesz się wyraźnie wyłączyć odłączanie sterownika urządzenia, musisz zabronić odłączania i zwalniania modułu.
Aby uniemożliwić odłączanie, ustaw flagę
suppress_bind_attrsnatruewstruct device_driversterownika. To ustawienie uniemożliwia wyświetlanie plikówbindiunbindw katalogusysfssterownika. Plikunbindumożliwia przestrzeni użytkownika wywołanie odłączenia sterownika od urządzenia.Aby uniemożliwić odładowanie modułu, upewnij się, że w
lsmodznajduje się[permanent]. Jeśli nie użyjeszmodule_exit()animodule_XXX_driver(), moduł zostanie oznaczony jako[permanent].
Nie wczytuj oprogramowania układowego w funkcji sondy
Sterownik nie powinien wczytywać oprogramowania z funkcji .probe(), ponieważ może nie mieć dostępu do oprogramowania, jeśli sterownik sprawdza urządzenie przed zamontowaniem systemu plików opartego na pamięci flash lub pamięci trwałej. W takich przypadkach interfejs request_firmware*() API może przez długi czas blokować działanie, a potem zakończyć się niepowodzeniem, co może niepotrzebnie spowolnić proces uruchamiania. Zamiast tego odłóż wczytywanie oprogramowania układowego do momentu, gdy klient zacznie korzystać z urządzenia. Na przykład sterownik wyświetlacza może wczytywać oprogramowanie sprzętowe po otwarciu urządzenia wyświetlającego.
Używanie .probe() do wczytywania oprogramowania sprzętowego może być w niektórych przypadkach dopuszczalne, np. w sterowniku zegara, który do działania potrzebuje oprogramowania sprzętowego, ale urządzenie nie jest dostępne dla przestrzeni użytkownika. Możliwe są też inne odpowiednie przypadki użycia.
Wdrażanie asynchronicznego sondowania
Obsługuj i używaj asynchronicznego sondowania, aby korzystać z przyszłych ulepszeń, takich jak równoległe ładowanie modułów lub sondowanie urządzeń w celu przyspieszenia rozruchu, które mogą zostać dodane do Androida w przyszłych wersjach. Moduły sterowników, które nie korzystają z asynchronicznego sondowania, mogą zmniejszyć skuteczność takich optymalizacji.
Aby oznaczyć sterownik jako obsługujący i preferujący sondowanie asynchroniczne, ustaw pole
probe_type w elemencie struct device_driver sterownika. Poniższy przykład pokazuje, jak włączyć taką obsługę w przypadku sterownika platformy:
static struct platform_driver acme_driver = {
.probe = acme_probe,
...
.driver = {
.name = "acme",
...
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
Aby sterownik działał z asynchronicznym sondowaniem, nie jest wymagany specjalny kod. Pamiętaj jednak o tych kwestiach podczas dodawania obsługi sondowania asynchronicznego.
Nie wyciągaj wniosków na temat wcześniej zbadanych zależności. Sprawdź bezpośrednio lub pośrednio (większość wywołań platformy) i zwróć wartość
-EPROBE_DEFER, jeśli co najmniej jeden dostawca nie jest jeszcze gotowy.Jeśli dodasz urządzenia dziecka w funkcji sondowania urządzenia rodzica, nie zakładaj, że urządzenia dziecka zostaną od razu zbadane.
Jeśli sonda zawiedzie, wykonaj odpowiednią obsługę błędów i wyczyść dane (patrz Używanie wariantów interfejsu API devm_*()).
Nie używaj MODULE_SOFTDEP do zamawiania sond urządzenia
Funkcja MODULE_SOFTDEP() nie jest niezawodnym rozwiązaniem gwarantującym kolejność sondowania urządzeń i nie należy jej używać z tych powodów:
Sonda odroczona Gdy moduł się wczytuje, test urządzenia może zostać odroczony, ponieważ jeden z jego dostawców nie jest gotowy. Może to prowadzić do niezgodności między kolejnością ładowania modułów a kolejnością sondowania urządzenia.
Jeden sterownik, wiele urządzeń Moduł sterownika może zarządzać określonym typem urządzenia. Jeśli system zawiera więcej niż 1 instancję typu urządzenia, a każde z tych urządzeń ma inne wymagania dotyczące kolejności sondowania, nie można spełnić tych wymagań za pomocą kolejności ładowania modułów.
Sondowanie asynchroniczne Moduły sterownika, które wykonują asynchroniczne sondowanie, nie sondują urządzenia od razu po załadowaniu modułu. Zamiast tego równoległy wątek obsługuje sondowanie urządzenia, co może prowadzić do niezgodności między kolejnością ładowania modułów a kolejnością sondowania urządzenia. Na przykład, gdy główny moduł sterownika I2C wykonuje asynchroniczne sondowanie, a moduł sterownika dotykowego zależy od układu PMIC na magistrali I2C, nawet jeśli sterownik dotykowy i sterownik układu PMIC wczytują się w odpowiedniej kolejności, sondowanie sterownika dotykowego może zostać podjęte przed sondowaniem sterownika układu PMIC.
Jeśli masz moduły sterowników korzystające z funkcji MODULE_SOFTDEP(), popraw je tak, aby nie używały tej funkcji. Aby Ci pomóc, zespół Androida wprowadził zmiany, które umożliwiają jądru obsługę problemów z kolejnością bez używania MODULE_SOFTDEP(). W szczególności możesz użyć fw_devlink, aby zapewnić kolejność sondowania, a po sondowaniu wszystkich odbiorców urządzenia użyć wywołania zwrotnego sync_state() do wykonania niezbędnych zadań.
W przypadku konfiguracji używaj #if IS_ENABLED() zamiast #ifdef
Używaj #if IS_ENABLED(CONFIG_XXX) zamiast #ifdef CONFIG_XXX, aby mieć pewność, że kod w bloku #if będzie nadal kompilowany, jeśli w przyszłości konfiguracja zmieni się na trójstanową. Różnice są następujące:
#if IS_ENABLED(CONFIG_XXX)przyjmuje wartośćtrue, gdyCONFIG_XXXma wartość module (=m) lub built-in (=y).#ifdef CONFIG_XXXprzyjmuje wartośćtrue, gdyCONFIG_XXXma wartość wbudowany (=y), ale nie przyjmuje jej, gdyCONFIG_XXXma wartość moduł (=m). Używaj tego tylko wtedy, gdy masz pewność, że chcesz wykonać tę samą czynność, gdy konfiguracja jest ustawiona na moduł lub jest wyłączona.
Używaj prawidłowego makra do kompilacji warunkowych
Jeśli CONFIG_XXX jest ustawiony na moduł (=m), system kompilacji automatycznie definiuje CONFIG_XXX_MODULE. Jeśli sterownik jest kontrolowany przez CONFIG_XXX i chcesz sprawdzić, czy jest kompilowany jako moduł, postępuj zgodnie z tymi wytycznymi:
W pliku C (lub dowolnym pliku źródłowym, który nie jest plikiem nagłówkowym) sterownika nie używaj
#ifdef CONFIG_XXX_MODULE, ponieważ jest to niepotrzebnie restrykcyjne i przestanie działać, jeśli nazwa konfiguracji zostanie zmieniona naCONFIG_XYZ. W przypadku każdego pliku źródłowego innego niż plik nagłówkowy, który jest kompilowany do modułu, system kompilacji automatycznie definiujeMODULEw zakresie tego pliku. Aby sprawdzić, czy plik C (lub dowolny plik źródłowy inny niż plik nagłówkowy) jest kompilowany w ramach modułu, użyj polecenia#ifdef MODULE(bez prefiksuCONFIG_).W przypadku plików nagłówkowych to samo sprawdzenie jest trudniejsze, ponieważ nie są one kompilowane bezpośrednio do pliku binarnego, ale raczej jako część pliku C (lub innych plików źródłowych). W przypadku plików nagłówkowych stosuj te reguły:
W przypadku pliku nagłówkowego, który używa
#ifdef MODULE, wynik zmienia się w zależności od tego, który plik źródłowy go używa. Oznacza to, że ten sam plik nagłówkowy w tej samej kompilacji może mieć różne części kodu skompilowane dla różnych plików źródłowych (moduł, wbudowany lub wyłączony). Może to być przydatne, gdy chcesz zdefiniować makro, które musi rozwijać się w jeden sposób w przypadku wbudowanego kodu, a w inny sposób w przypadku modułu.W przypadku pliku nagłówkowego, który musi być kompilowany w części kodu, gdy określony
CONFIG_XXXjest ustawiony na moduł (niezależnie od tego, czy plik źródłowy, który go zawiera, jest modułem), plik nagłówkowy musi używać#ifdef CONFIG_XXX_MODULE.