Mit den folgenden Richtlinien können Sie die Robustheit und Zuverlässigkeit Ihrer Anbietermodule erhöhen. Wenn Sie viele Richtlinien befolgen, kann es einfacher sein, die richtige Reihenfolge für das Laden von Modulen und die Reihenfolge zu bestimmen, in der Treiber Geräte prüfen müssen.
Ein Modul kann eine Bibliothek oder ein Treiber sein.
Bibliotheksmodule sind Bibliotheken, die APIs für die Verwendung durch andere Module bereitstellen. Solche Module sind in der Regel nicht hardwarespezifisch. Beispiele für Bibliotheksmodule sind ein AES-Verschlüsselungsmodul, das
remoteproc
-Framework, das als Modul kompiliert wird, und ein Logbuffer-Modul. Der Modulcode inmodule_init()
wird ausgeführt, um Datenstrukturen einzurichten. Anderer Code wird nur ausgeführt, wenn er von einem externen Modul ausgelöst wird.Treibermodule sind Treiber, die nach einem bestimmten Gerätetyp suchen oder sich an ihn binden. Solche Module sind hardwarespezifisch. Beispiele für Treibermodule sind UART, PCIe und Hardware für Video-Encoder. Treibermodule werden nur aktiviert, wenn das zugehörige Gerät im System vorhanden ist.
Wenn das Gerät nicht vorhanden ist, wird nur der Modulcode
module_init()
ausgeführt, der den Treiber beim Driver Core-Framework registriert.Wenn das Gerät vorhanden ist und der Treiber es erfolgreich erkennt oder daran gebunden wird, wird möglicherweise anderer Modulcode ausgeführt.
Modulinitialisierung und ‑beendigung richtig verwenden
Treibermodule müssen einen Treiber in module_init()
registrieren und einen Treiber in module_exit()
deregistrieren. Eine Möglichkeit, diese Einschränkungen zu erzwingen, besteht darin, Wrapper-Makros zu verwenden. So wird die direkte Verwendung von module_init()
-, *_initcall()
- oder module_exit()
-Makros vermieden.
Verwenden Sie
module_subsystem_driver()
für Module, die entladen werden können. Beispiele:module_platform_driver()
,module_i2c_driver()
undmodule_pci_driver()
.Verwenden Sie für Module, die nicht entladen werden können,
builtin_subsystem_driver()
. Beispiele:builtin_platform_driver()
,builtin_i2c_driver()
undbuiltin_pci_driver()
.
In einigen Treibermodulen werden module_init()
und module_exit()
verwendet, da sie mehr als einen Treiber registrieren. Wenn Sie ein Treibermodul verwenden, in dem mit module_init()
und module_exit()
mehrere Treiber registriert werden, sollten Sie versuchen, die Treiber in einem einzigen Treiber zu kombinieren. Sie können beispielsweise den String compatible
oder die Hilfsdaten des Geräts verwenden, anstatt separate Treiber zu registrieren.
Alternativ können Sie das Treibermodul in zwei Module aufteilen.
Ausnahmen bei Init- und Exit-Funktionen
In Bibliotheksmodulen werden keine Treiber registriert und sie sind von Einschränkungen für module_init()
und module_exit()
ausgenommen, da diese Funktionen möglicherweise zum Einrichten von Datenstrukturen, Arbeitswarteschlangen oder Kernel-Threads erforderlich sind.
Makro „MODULE_DEVICE_TABLE“ verwenden
Treibermodule müssen das Makro MODULE_DEVICE_TABLE
enthalten, damit der Nutzerbereich die von einem Treibermodul unterstützten Geräte ermitteln kann, bevor das Modul geladen wird. Android kann diese Daten verwenden, um das Laden von Modulen zu optimieren, z. B. um das Laden von Modulen für Geräte zu vermeiden, die nicht im System vorhanden sind. Beispiele für die Verwendung des Makros finden Sie im Upstream-Code.
CRC-Fehler aufgrund von vorab deklarierten Datentypen vermeiden
Fügen Sie keine Headerdateien ein, um Informationen zu vorab deklarierten Datentypen zu erhalten.
Einige in einer Headerdatei (header-A.h
) definierte Strukturen, Unions und andere Datentypen können in einer anderen Headerdatei (header-B.h
) vorab deklariert werden, in der in der Regel Zeiger auf diese Datentypen verwendet werden. Dieses Codemuster bedeutet, dass der Kernel die Datenstruktur absichtlich für die Nutzer von header-B.h
privat halten möchte.
Nutzer von header-B.h
sollten header-A.h
nicht verwenden, um direkt auf die Interna dieser vorab deklarierten Datenstrukturen zuzugreifen. Dies führt zu CONFIG_MODVERSIONS
-CRC-Fehlern (die ABI-Konformitätsprobleme verursachen), wenn ein anderer Kernel (z. B. der GKI-Kernel) versucht, das Modul zu laden.
struct fwnode_handle
ist beispielsweise in include/linux/fwnode.h
definiert, wird aber in include/linux/device.h
als struct fwnode_handle;
deklariert, da der Kernel die Details von struct fwnode_handle
vor den Nutzern von include/linux/device.h
schützen möchte. In diesem Szenario müssen Sie #include <linux/fwnode.h>
nicht in einem Modul hinzufügen, um Zugriff auf Mitglieder von struct fwnode_handle
zu erhalten. Jedes Design, in dem Sie solche Headerdateien einfügen müssen, weist auf ein schlechtes Designmuster hin.
Nicht direkt auf die wichtigsten Kernelstrukturen zugreifen
Der direkte Zugriff auf oder die direkte Änderung von wichtigen Kernel-Datenstrukturen kann zu unerwünschtem Verhalten führen, z. B. zu Speicherlecks, Abstürzen und Inkompatibilität mit zukünftigen Kernel-Versionen. Eine Datenstruktur ist eine zentrale Kernel-Datenstruktur, wenn eine der folgenden Bedingungen erfüllt ist:
Die Datenstruktur ist unter
KERNEL-DIR/include/
definiert. Beispiel:struct device
undstruct dev_links_info
. Datenstrukturen, die ininclude/linux/soc
definiert sind, sind ausgenommen.Die Datenstruktur wird vom Modul zugewiesen oder initialisiert, aber für den Kernel sichtbar gemacht, indem sie indirekt (über einen Zeiger in einer Struktur) oder direkt als Eingabe in eine vom Kernel exportierte Funktion übergeben wird. Ein
cpufreq
-Treibermodul initialisiert beispielsweise denstruct cpufreq_driver
und übergibt ihn dann als Eingabe ancpufreq_register_driver()
. Danach sollte dascpufreq
-Treibermodulstruct cpufreq_driver
nicht direkt ändern, da durch den Aufruf voncpufreq_register_driver()
struct cpufreq_driver
für den Kernel sichtbar wird.Die Datenstruktur wird nicht von Ihrem Modul initialisiert. Beispiel:
struct regulator_dev
wird vonregulator_register()
zurückgegeben.
Greifen Sie nur über Funktionen, die vom Kernel exportiert werden, oder über Parameter, die explizit als Eingabe an Vendor-Hooks übergeben werden, auf die wichtigsten Kernel-Datenstrukturen zu. Wenn Sie keinen API- oder Anbieter-Hook haben, um Teile einer wichtigen Kernel-Datenstruktur zu ändern, ist das wahrscheinlich beabsichtigt und Sie sollten die Datenstruktur nicht über Module ändern. Ändern Sie beispielsweise keine Felder in struct device
oder struct device.links
.
Verwenden Sie eine
devm_*()
-Funktion wiedevm_clk_get()
,devm_regulator_get()
oderdevm_kzalloc()
, umdevice.devres_head
zu ändern.Wenn Sie Felder in
struct device.links
ändern möchten, verwenden Sie eine Geräteverbindungs-API wiedevice_link_add()
oderdevice_link_del()
.
Gerätebaumknoten mit kompatiblem Attribut nicht parsen
Wenn ein Gerätebaumknoten die Eigenschaft compatible
hat, wird automatisch oder wenn of_platform_populate()
für den übergeordneten Gerätebaumknoten aufgerufen wird (in der Regel vom Gerätetreiber des übergeordneten Geräts), ein struct device
dafür zugewiesen. Die Standarderwartung (mit Ausnahme einiger Geräte, die frühzeitig für den Scheduler initialisiert werden) ist, dass ein Gerätebaumknoten mit der Eigenschaft compatible
einen struct device
und einen passenden Gerätetreiber hat. Alle anderen Ausnahmen werden bereits vom Upstream-Code verarbeitet.
Außerdem werden in fw_devlink
(früher of_devlink
) Gerätebaumknoten mit der Property compatible
als Geräte mit einem zugewiesenen struct device
betrachtet, der von einem Treiber abgefragt wird. Wenn ein Gerätebaumknoten die Eigenschaft compatible
hat, die zugewiesene struct device
aber nicht geprüft wird, kann fw_devlink
verhindern, dass die zugehörigen Geräte geprüft werden, oder verhindern, dass sync_state()
-Aufrufe für die zugehörigen Geräte des Anbieters aufgerufen werden.
Wenn Ihr Treiber eine of_find_*()
-Funktion (z. B. of_find_node_by_name()
oder of_find_compatible_node()
) verwendet, um direkt einen Gerätebaumknoten mit der Eigenschaft compatible
zu finden und dann zu parsen, müssen Sie das Modul korrigieren. Schreiben Sie dazu einen Gerätetreiber, der das Gerät testen kann, oder entfernen Sie die Eigenschaft compatible
(nur möglich, wenn sie noch nicht in den Upstream-Quellcode aufgenommen wurde). Wenn Sie Alternativen besprechen möchten, wenden Sie sich unter kernel-team@android.com an das Android-Kernel-Team. Halten Sie sich bereit, Ihre Anwendungsfälle zu begründen.
Lieferanten mit Gerätebaum-Phandles suchen
Verweisen Sie nach Möglichkeit in DT mit einem phandle (einem Verweis oder Zeiger auf einen DT-Knoten) auf einen Anbieter. Durch die Verwendung von Standard-DT-Bindungen und Phandles zum Verweisen auf Lieferanten kann fw_devlink
(zuvor of_devlink
) durch Parsen des Gerätebaums zur Laufzeit automatisch geräteübergreifende Abhängigkeiten ermitteln. Der Kernel kann dann Geräte automatisch in der richtigen Reihenfolge testen, sodass keine Modulladereihenfolge oder MODULE_SOFTDEP()
erforderlich ist.
Altes Szenario (keine Gerätebaumunterstützung im ARM-Kernel)
Bevor die Unterstützung von Gerätebäumen in ARM-Kerneln hinzugefügt wurde, suchten Verbraucher wie Touch-Geräte Anbieter wie Regler mithilfe von global eindeutigen Strings.
Beispielsweise könnte der ACME PMIC-Treiber mehrere Regler (z. B. acme-pmic-ldo1
bis acme-pmic-ldo10
) registrieren oder ankündigen und ein Touch-Treiber könnte einen Regler mit regulator_get(dev, "acme-pmic-ldo10")
suchen.
Auf einem anderen Board kann der LDO8 jedoch das Touch-Gerät mit Strom versorgen. Dies führt zu einem umständlichen System, in dem derselbe Touch-Treiber für jedes Board, auf dem das Touch-Gerät verwendet wird, den richtigen Lookup-String für den Regler ermitteln muss.
Aktuelles Szenario (Gerätebaumunterstützung im ARM-Kernel)
Nachdem die Unterstützung für Gerätebäume in ARM-Kernel aufgenommen wurde, können Verbraucher Lieferanten im Gerätebaum identifizieren, indem sie mit einem phandle auf den Gerätebaumknoten des Lieferanten verweisen.
Verbraucher können die Ressource auch danach benennen, wofür sie verwendet wird, anstatt danach, wer sie liefert. Der Touch-Treiber aus dem vorherigen Beispiel könnte beispielsweise regulator_get(dev, "core")
und regulator_get(dev, "sensor")
verwenden, um die Zulieferer zu ermitteln, die den Kern und den Sensor des Touch-Geräts mit Strom versorgen. Der zugehörige Gerätebaum für ein solches Gerät ähnelt dem folgenden Codebeispiel:
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 {
...
};
};
Worst-of-both-worlds-Szenario
Einige Treiber, die von älteren Kernels portiert wurden, enthalten Legacy-Verhalten im Gerätebaum, das den schlechtesten Teil des Legacy-Schemas auf das neuere Schema erzwingt, das die Dinge eigentlich einfacher machen soll. In solchen Treibern liest der Consumer-Treiber den String für die Suche über eine gerätespezifische Gerätebaumstruktur-Eigenschaft. Der Anbieter verwendet eine andere anbieterspezifische Eigenschaft, um den Namen für die Registrierung der Anbieterressource zu definieren. Consumer und Anbieter verwenden dann weiterhin das alte Schema, bei dem Strings für die Suche nach dem Anbieter verwendet werden. In diesem Worst-Case-Szenario:
Der Touch-Treiber verwendet Code, der dem folgenden ähnelt:
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);
Im Gerätebaum wird Code ähnlich dem folgenden verwendet:
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" }; };
Framework-API-Fehler nicht ändern
Framework-APIs wie regulator
, clocks
, irq
, gpio
, phys
und extcon
geben -EPROBE_DEFER
als Fehler-Rückgabewert zurück, um anzugeben, dass ein Gerät versucht, eine Anfrage zu senden, dies aber derzeit nicht möglich ist und der Kernel die Anfrage später noch einmal versuchen sollte. Damit die .probe()
-Funktion Ihres Geräts in solchen Fällen wie erwartet fehlschlägt, dürfen Sie den Fehlerwert nicht ersetzen oder neu zuordnen.
Wenn Sie den Fehlerwert ersetzen oder neu zuordnen, kann es sein, dass -EPROBE_DEFER
nicht mehr berücksichtigt wird und Ihr Gerät nie abgefragt wird.
devm_*()-API-Varianten verwenden
Wenn das Gerät eine Ressource über eine devm_*()
-API abruft, wird die Ressource automatisch vom Kernel freigegeben, wenn das Gerät nicht erkannt wird oder erfolgreich erkannt und später entbunden wird. Diese Funktion macht den Fehlerbehandlungscode in der Funktion probe()
übersichtlicher, da keine goto
-Sprünge erforderlich sind, um die von devm_*()
erworbenen Ressourcen freizugeben, und vereinfacht das Aufheben der Bindung von Treibern.
Aufheben der Bindung von Gerätetreibern verarbeiten
Achten Sie darauf, dass Sie Gerätetreiber bewusst entbinden, und lassen Sie die Entbindung nicht undefiniert, da „undefiniert“ nicht „nicht zulässig“ bedeutet. Sie müssen entweder die Bindung von Gerätetreibern vollständig implementieren oder die Bindung von Gerätetreibern explizit deaktivieren.
Bindung von Gerät und Treiber aufheben
Wenn Sie sich für die vollständige Implementierung der Aufhebung der Bindung von Gerätetreibern entscheiden, heben Sie die Bindung von Gerätetreibern sauber auf, um Speicher- oder Ressourcenlecks und Sicherheitsprobleme zu vermeiden. Sie können ein Gerät an einen Treiber binden, indem Sie die probe()
-Funktion des Treibers aufrufen, und ein Gerät entbinden, indem Sie die remove()
-Funktion des Treibers aufrufen. Wenn keine remove()
-Funktion vorhanden ist, kann der Kernel das Gerät trotzdem entbinden. Der Treiberkern geht davon aus, dass der Treiber keine Bereinigungsarbeiten durchführen muss, wenn er die Bindung an das Gerät aufhebt. Ein Treiber, der von einem Gerät getrennt wurde, muss keine expliziten Bereinigungsarbeiten durchführen, wenn beide der folgenden Bedingungen zutreffen:
Alle Ressourcen, die von der
probe()
-Funktion eines Treibers abgerufen werden, erfolgen überdevm_*()
-APIs.Das Hardwaregerät benötigt keine Herunterfahr- oder Quiescing-Sequenz.
In diesem Fall gibt der Treiberkern alle Ressourcen frei, die über devm_*()
-APIs abgerufen wurden. Wenn eine der beiden vorherigen Aussagen nicht zutrifft, muss der Treiber beim Unbinden von einem Gerät Ressourcen freigeben und die Hardware herunterfahren oder in den Ruhezustand versetzen. Verwenden Sie eine der folgenden Optionen, um sicherzustellen, dass ein Gerät ein Treibermodul sauber trennen kann:
Wenn für die Hardware keine Shutdown- oder Quiescing-Sequenz erforderlich ist, ändern Sie das Gerätemodul so, dass Ressourcen mithilfe von
devm_*()
-APIs abgerufen werden.Implementieren Sie den
remove()
-Treiberbetrieb in derselben Struktur wie dieprobe()
-Funktion und führen Sie dann die Bereinigungsschritte mit derremove()
-Funktion aus.
Aufheben der Bindung von Gerätetreibern explizit deaktivieren (nicht empfohlen)
Wenn Sie das explizite Aufheben der Bindung von Gerätetreibern deaktivieren möchten, müssen Sie das Aufheben der Bindung und das Entladen von Modulen deaktivieren.
Wenn Sie das Aufheben der Bindung nicht zulassen möchten, setzen Sie das Flag
suppress_bind_attrs
imstruct device_driver
des Treibers auftrue
. Dadurch wird verhindert, dass die Dateienbind
undunbind
im Verzeichnissysfs
des Treibers angezeigt werden. Die Dateiunbind
ermöglicht es dem Nutzerbereich, das Unbinding eines Treibers von seinem Gerät auszulösen.Wenn Sie das Entladen von Modulen nicht zulassen möchten, muss das Modul
[permanent]
inlsmod
haben. Wenn Siemodule_exit()
odermodule_XXX_driver()
nicht verwenden, wird das Modul als[permanent]
gekennzeichnet.
Firmware nicht über die Probe-Funktion laden
Der Treiber sollte die Firmware nicht über die .probe()
-Funktion laden, da er möglicherweise keinen Zugriff auf die Firmware hat, wenn der Treiber vor dem Mounten des Dateisystems auf Flash- oder permanentem Speicher basiert. In solchen Fällen kann die request_firmware*()
API lange blockieren und dann fehlschlagen, was den Bootvorgang unnötig verlangsamen kann. Verschieben Sie das Laden der Firmware stattdessen auf den Zeitpunkt, zu dem ein Client das Gerät verwendet. Ein Displaytreiber könnte beispielsweise die Firmware laden, wenn das Displaygerät geöffnet wird.
Die Verwendung von .probe()
zum Laden von Firmware ist in einigen Fällen in Ordnung, z. B. in einem Taktgeber-Treiber, der Firmware benötigt, um zu funktionieren, das Gerät aber nicht für den Nutzerbereich verfügbar ist. Andere geeignete Anwendungsfälle sind möglich.
Asynchrones Probing implementieren
Unterstützen und verwenden Sie asynchrone Tests, um von zukünftigen Verbesserungen wie dem parallelen Laden von Modulen oder Gerätetests zur Beschleunigung der Bootzeit zu profitieren, die möglicherweise in zukünftigen Releases zu Android hinzugefügt werden. Treibermodule, die keine asynchrone Suche verwenden, können die Effektivität solcher Optimierungen verringern.
Wenn Sie einen Treiber als unterstützend und bevorzugt für asynchrone Tests markieren möchten, legen Sie das Feld probe_type
im struct device_driver
-Member des Treibers fest. Im folgenden Beispiel ist die Unterstützung für einen Plattformtreiber aktiviert:
static struct platform_driver acme_driver = {
.probe = acme_probe,
...
.driver = {
.name = "acme",
...
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
Damit ein Treiber mit asynchronen Tests funktioniert, ist kein spezieller Code erforderlich. Beachten Sie jedoch Folgendes, wenn Sie Unterstützung für asynchrones Probing hinzufügen.
Gehen Sie nicht davon aus, dass zuvor untersuchte Abhängigkeiten weiterhin bestehen. Direkt oder indirekt (die meisten Framework-Aufrufe) prüfen und
-EPROBE_DEFER
zurückgeben, wenn ein oder mehrere Anbieter noch nicht bereit sind.Wenn Sie Kindergeräte in die Probe-Funktion eines Elternteils einfügen, gehen Sie nicht davon aus, dass die Kindergeräte sofort geprüft werden.
Wenn ein Test fehlschlägt, führen Sie eine ordnungsgemäße Fehlerbehandlung und Bereinigung durch (siehe devm_*()-API-Varianten verwenden).
MODULE_SOFTDEP nicht zum Bestellen von Geräte-Probes verwenden
Die Funktion MODULE_SOFTDEP()
ist keine zuverlässige Lösung, um die Reihenfolge der Geräte-Probes zu garantieren, und darf aus den folgenden Gründen nicht verwendet werden.
Verzögerte Prüfung: Wenn ein Modul geladen wird, kann die Geräte-Probe verzögert werden, weil einer der Anbieter noch nicht bereit ist. Dies kann zu einer Abweichung zwischen der Reihenfolge, in der die Module geladen werden, und der Reihenfolge, in der die Geräte untersucht werden, führen.
Ein Treiber, viele Geräte. Ein Treibermodul kann einen bestimmten Gerätetyp verwalten. Wenn das System mehrere Instanzen eines Gerätetyps enthält und für diese Geräte jeweils eine andere Reihenfolge für die Probe erforderlich ist, können Sie diese Anforderungen nicht durch die Reihenfolge beim Laden von Modulen erfüllen.
Asynchrones Testen: Treibermodule, die asynchrone Tests durchführen, testen ein Gerät nicht sofort, wenn das Modul geladen wird. Stattdessen wird das Erfassen von Geräten in einem parallelen Thread ausgeführt, was zu einer Abweichung zwischen der Reihenfolge des Modulladens und der Reihenfolge des Erfassens von Geräten führen kann. Wenn beispielsweise ein I2C-Haupttreibermodul asynchrones Probing durchführt und ein Touch-Treibermodul vom PMIC im I2C-Bus abhängt, kann es sein, dass das Probing des Touch-Treibers vor dem Probing des PMIC-Treibers versucht wird, auch wenn der Touch-Treiber und der PMIC-Treiber in der richtigen Reihenfolge geladen werden.
Wenn Sie Treibermodule mit der Funktion MODULE_SOFTDEP()
haben, müssen Sie sie so korrigieren, dass diese Funktion nicht verwendet wird. Das Android-Team hat Änderungen vorgenommen, damit der Kernel Reihenfolgeprobleme ohne MODULE_SOFTDEP()
verarbeiten kann. Mit fw_devlink
können Sie die Reihenfolge der Probes festlegen und nach dem Probing aller Geräte mit dem sync_state()
-Callback alle erforderlichen Aufgaben ausführen.
#if IS_ENABLED() anstelle von #ifdef für Konfigurationen verwenden
Verwenden Sie #if IS_ENABLED(CONFIG_XXX)
anstelle von #ifdef CONFIG_XXX
, damit der Code im #if
-Block weiterhin kompiliert wird, wenn sich die Konfiguration in Zukunft in eine Tristate-Konfiguration ändert. Es bestehen jedoch folgende Unterschiede:
#if IS_ENABLED(CONFIG_XXX)
wird alstrue
ausgewertet, wennCONFIG_XXX
auf „module“ (=m
) oder „built-in“ (=y
) festgelegt ist.#ifdef CONFIG_XXX
wird zutrue
ausgewertet, wennCONFIG_XXX
auf „built-in“ (=y
) festgelegt ist, aber nicht, wennCONFIG_XXX
auf „module“ (=m
) festgelegt ist. Verwenden Sie diese Option nur, wenn Sie sicher sind, dass Sie dasselbe tun möchten, wenn die Konfiguration auf „module“ festgelegt oder deaktiviert ist.
Das richtige Makro für bedingte Kompilierungen verwenden
Wenn CONFIG_XXX
auf „module“ (=m
) festgelegt ist, definiert das Build-System automatisch CONFIG_XXX_MODULE
. Wenn Ihr Treiber von CONFIG_XXX
gesteuert wird und Sie prüfen möchten, ob er als Modul kompiliert wird, folgen Sie diesen Richtlinien:
Verwenden Sie in der C-Datei (oder einer anderen Quelldatei, die keine Headerdatei ist) für Ihren Treiber nicht
#ifdef CONFIG_XXX_MODULE
, da dies unnötig restriktiv ist und die Datei nicht mehr funktioniert, wenn die Konfiguration inCONFIG_XYZ
umbenannt wird. Für jede Nicht-Header-Quelldatei, die in ein Modul kompiliert wird, definiert das Build-System automatischMODULE
für den Bereich dieser Datei. Wenn Sie also prüfen möchten, ob eine C-Datei (oder eine andere Nicht-Header-Quelldatei) als Teil eines Moduls kompiliert wird, verwenden Sie#ifdef MODULE
(ohne das PräfixCONFIG_
).In Headerdateien ist dieselbe Prüfung schwieriger, da Headerdateien nicht direkt in eine Binärdatei kompiliert werden, sondern als Teil einer C-Datei (oder anderer Quelldateien). Beachten Sie die folgenden Regeln für Headerdateien:
Bei einer Headerdatei, in der
#ifdef MODULE
verwendet wird, ändert sich das Ergebnis je nach der Quelldatei, in der sie verwendet wird. Das bedeutet, dass dieselbe Headerdatei im selben Build unterschiedliche Teile ihres Codes für verschiedene Quelldateien (Modul im Vergleich zu integriert oder deaktiviert) kompiliert haben kann. Das kann nützlich sein, wenn Sie ein Makro definieren möchten, das für integrierten Code auf eine bestimmte Weise und für ein Modul auf eine andere Weise erweitert werden soll.Wenn für eine Headerdatei ein bestimmter
CONFIG_XXX
auf „module“ gesetzt ist (unabhängig davon, ob die Quelldatei, die sie enthält, ein Modul ist), muss die Headerdatei#ifdef CONFIG_XXX_MODULE
verwenden.