Verwenden Sie die folgenden Richtlinien, um die Robustheit und Zuverlässigkeit Ihrer Anbietermodule zu erhöhen. Viele Richtlinien können, wenn sie befolgt werden, dazu beitragen, die richtige Modulladereihenfolge und die Reihenfolge, in der Treiber nach Geräten suchen müssen, leichter zu bestimmen.
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 normalerweise nicht hardwarespezifisch. Beispiele für Bibliotheksmodule sind ein AES-Verschlüsselungsmodul, das als Modul kompilierte
remoteproc
Framework und ein Logbuffer-Modul. Der Modulcode inmodule_init()
wird ausgeführt, um Datenstrukturen einzurichten, aber kein anderer Code wird ausgeführt, es sei denn, er wird durch ein externes Modul ausgelöst.Treibermodule sind Treiber, die nach einem bestimmten Gerätetyp suchen oder sich an diesen binden. Solche Module sind hardwarespezifisch. Beispiele für Treibermodule sind UART-, PCIe- und Video-Encoder-Hardware. Treibermodule werden nur aktiviert, wenn das zugehörige Gerät im System vorhanden ist.
Wenn das Gerät nicht vorhanden ist, wird als einziger Modulcode der
module_init()
Code ausgeführt, der den Treiber beim Treiberkern-Framework registriert.Wenn das Gerät vorhanden ist und der Treiber erfolgreich nach diesem Gerät sucht oder sich daran bindet, wird möglicherweise anderer Modulcode ausgeführt.
Modul-Init/Exit korrekt verwenden
Treibermodule müssen einen Treiber in module_init()
registrieren und die Registrierung eines Treibers in module_exit()
aufheben. Eine einfache Möglichkeit, diese Einschränkungen durchzusetzen, ist die Verwendung von Wrapper-Makros, wodurch die direkte Verwendung der Makros module_init()
, *_initcall()
oder module_exit()
vermieden wird.
Für Module, die entladen werden können, verwenden Sie
module_ subsystem _driver()
. Beispiele:module_platform_driver()
,module_i2c_driver()
undmodule_pci_driver()
.Für Module, die nicht entladen werden können, verwenden Sie
builtin_ subsystem _driver()
. Beispiele:builtin_platform_driver()
,builtin_i2c_driver()
undbuiltin_pci_driver()
.
Einige Treibermodule verwenden module_init()
und module_exit()
, weil sie mehr als einen Treiber registrieren. Versuchen Sie bei einem Treibermodul, das module_init()
und module_exit()
zum Registrieren mehrerer Treiber verwendet, die Treiber in einem einzigen Treiber zu kombinieren. Sie könnten beispielsweise anhand der compatible
Zeichenfolge oder der Aux-Daten des Geräts unterscheiden, anstatt separate Treiber zu registrieren. Alternativ können Sie das Treibermodul in zwei Module aufteilen.
Ausnahmen für Init- und Exit-Funktionen
Bibliotheksmodule registrieren keine Treiber und sind von den Einschränkungen für module_init()
und module_exit()
ausgenommen, da sie diese Funktionen möglicherweise zum Einrichten von Datenstrukturen, Arbeitswarteschlangen oder Kernel-Threads benötigen.
Verwenden Sie das Makro MODULE_DEVICE_TABLE
Treibermodule müssen das Makro MODULE_DEVICE_TABLE
enthalten, das es dem Benutzerbereich ermöglicht, die von einem Treibermodul unterstützten Geräte zu ermitteln, bevor das Modul geladen wird. Android kann diese Daten verwenden, um das Laden von Modulen zu optimieren, um beispielsweise das Laden von Modulen für Geräte zu vermeiden, die nicht im System vorhanden sind. Beispiele zur Verwendung des Makros finden Sie im Upstream-Code.
Vermeiden Sie CRC-Nichtübereinstimmungen aufgrund vorwärts deklarierter Datentypen
Schließen Sie keine Header-Dateien ein, um Einblick in vorwärts deklarierte Datentypen zu erhalten. Einige Strukturen, Unions und andere Datentypen, die in einer Header-Datei ( header-Ah
) definiert sind, können in einer anderen Header-Datei ( header-Bh
) vorwärtsdeklariert werden, die normalerweise Zeiger auf diese Datentypen verwendet. Dieses Codemuster bedeutet, dass der Kernel absichtlich versucht, die Datenstruktur für die Benutzer von header-Bh
privat zu halten.
Benutzer von header-Bh
sollten header-Ah
nicht einschließen, um direkt auf die Interna dieser vorwärtsdeklarierten Datenstrukturen zuzugreifen. Dies führt zu CONFIG_MODVERSIONS
CRC-Nichtübereinstimmungsproblemen (die ABI-Konformitätsprobleme erzeugen), wenn ein anderer Kernel (z. B. der GKI-Kernel) versucht, das Modul zu laden.
Beispielsweise ist struct fwnode_handle
in include/linux/fwnode.h
definiert, wird aber als struct fwnode_handle;
in include/linux/device.h
, weil der Kernel versucht, die Details von struct fwnode_handle
vor den Benutzern von include/linux/device.h
geheim zu halten. Fügen Sie in diesem Szenario nicht #include <linux/fwnode.h>
in einem Modul hinzu, um Zugriff auf Mitglieder von struct fwnode_handle
zu erhalten. Jedes Design, in das Sie solche Header-Dateien einbinden müssen, weist auf ein schlechtes Designmuster hin.
Greifen Sie nicht direkt auf Kernelstrukturen zu
Der direkte Zugriff auf oder die Änderung von Kernel-Datenstrukturen kann zu unerwünschtem Verhalten führen, einschließlich Speicherlecks, Abstürzen und beeinträchtigter Kompatibilität mit zukünftigen Kernel-Versionen. Eine Datenstruktur ist eine Kernel-Datenstruktur, wenn sie eine der folgenden Bedingungen erfüllt:
Die Datenstruktur ist unter
KERNEL-DIR /include/
definiert. Zum Beispielstruct 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. Beispielsweise initialisiert ein
cpufreq
Treibermodul diestruct cpufreq_driver
und übergibt sie dann als Eingabe ancpufreq_register_driver()
. Nach diesem Zeitpunkt sollte dascpufreq
Treibermodulstruct cpufreq_driver
nicht mehr direkt ändern, da der Aufruf voncpufreq_register_driver()
struct cpufreq_driver
für den Kernel sichtbar macht.Die Datenstruktur wird von Ihrem Modul nicht initialisiert. Beispielsweise wird
struct regulator_dev
vonregulator_register()
zurückgegeben.
Greifen Sie nur über vom Kernel exportierte Funktionen oder über Parameter, die explizit als Eingabe an Hersteller-Hooks übergeben werden, auf Kernel-Datenstrukturen zu. Wenn Sie nicht über eine API oder einen Hersteller-Hook zum Ändern von Teilen einer Kernel-Datenstruktur verfügen, ist dies wahrscheinlich beabsichtigt und Sie sollten die Datenstruktur nicht über Module ändern. Ändern Sie beispielsweise keine Felder in struct device
oder struct device.links
.
Um
device.devres_head
zu ändern, verwenden Sie einedevm_*()
Funktion wiedevm_clk_get()
,devm_regulator_get()
oderdevm_kzalloc()
.Um Felder in
struct device.links
zu ändern, verwenden Sie eine Device-Link-API wiedevice_link_add()
oderdevice_link_del()
.
Analysieren Sie keine Devicetree-Knoten mit der Eigenschaft „Compatible“.
Wenn ein Gerätebaumknoten (DT) über eine compatible
Eigenschaft verfügt, wird ihm automatisch oder beim Aufruf of_platform_populate()
auf dem übergeordneten DT-Knoten ein struct device
zugewiesen (normalerweise durch den Gerätetreiber des übergeordneten Geräts). Die Standarderwartung (mit Ausnahme einiger Geräte, die früh für den Scheduler initialisiert werden) ist, dass ein DT-Knoten mit einer compatible
Eigenschaft über ein struct device
und einen passenden Gerätetreiber verfügt. Alle anderen Ausnahmen werden bereits vom Upstream-Code behandelt.
Darüber hinaus betrachtet fw_devlink
(zuvor of_devlink
genannt) DT-Knoten mit der compatible
Eigenschaft als Geräte mit einem zugewiesenen struct device
, das von einem Treiber geprüft wird. Wenn ein DT-Knoten über eine compatible
Eigenschaft verfügt, das zugewiesene struct device
jedoch nicht geprüft wird, könnte fw_devlink
die Prüfung seiner Verbrauchergeräte oder den Aufruf von sync_state()
Aufrufen für seine Lieferantengeräte blockieren.
Wenn Ihr Treiber eine of_find_*()
Funktion (z. B. of_find_node_by_name()
oder of_find_compatible_node()
) verwendet, um direkt einen DT-Knoten zu finden, der eine compatible
Eigenschaft hat, und diesen DT-Knoten dann zu analysieren, reparieren Sie das Modul, indem Sie einen Gerätetreiber schreiben, der Sondierungen durchführen kann Entfernen Sie das Gerät oder entfernen Sie die compatible
Eigenschaft (nur möglich, wenn kein Upstream durchgeführt wurde). Um Alternativen zu besprechen, wenden Sie sich an das Android-Kernel-Team unter kernel-team@android.com und seien Sie bereit, Ihre Anwendungsfälle zu begründen.
Verwenden Sie DT-Phandles, um nach Lieferanten zu suchen
Verweisen Sie nach Möglichkeit auf einen Lieferanten, indem Sie in DT ein Phandle (eine Referenz/einen Zeiger auf einen DT-Knoten) verwenden. Durch die Verwendung standardmäßiger DT-Bindungen und Phandles zum Verweisen auf Lieferanten kann fw_devlink
(früher of_devlink
) automatisch Abhängigkeiten zwischen Geräten ermitteln, indem der DT zur Laufzeit analysiert wird. Der Kernel kann dann Geräte automatisch in der richtigen Reihenfolge prüfen, sodass keine Modulladereihenfolge oder MODULE_SOFTDEP()
erforderlich ist.
Legacy-Szenario (keine DT-Unterstützung im ARM-Kernel)
Zuvor, bevor DT-Unterstützung zu ARM-Kerneln hinzugefügt wurde, suchten Verbraucher wie Touch-Geräte mithilfe global eindeutiger Zeichenfolgen nach Lieferanten wie Regulierungsbehörden. Beispielsweise könnte der ACME-PMIC-Treiber mehrere Regler registrieren oder ankündigen (z. B. acme-pmic-ldo1
bis acme-pmic-ldo10
), und ein Touch-Treiber könnte mithilfe regulator_get(dev, "acme-pmic-ldo10")
einem Regler suchen. . Auf einer anderen Platine könnte jedoch der LDO8 das Touch-Gerät versorgen, wodurch ein umständliches System entsteht, bei dem derselbe Touch-Treiber die richtige Suchzeichenfolge für den Regler für jede Platine ermitteln muss, auf der das Touch-Gerät verwendet wird.
Aktuelles Szenario (DT-Unterstützung im ARM-Kernel)
Nachdem DT-Unterstützung zu ARM-Kerneln hinzugefügt wurde, können Verbraucher Lieferanten im DT identifizieren, indem sie mithilfe eines Phandle auf den Gerätebaumknoten des Lieferanten verweisen. Verbraucher können die Ressource auch nach dem Zweck benennen, für den sie verwendet wird, und nicht danach, wer sie bereitstellt. Der Touch-Treiber aus dem vorherigen Beispiel könnte beispielsweise regulator_get(dev, "core")
und regulator_get(dev, "sensor")
verwenden, um die Lieferanten abzurufen, die den Kern und Sensor des Touch-Geräts mit Strom versorgen. Der zugehörige DT 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 {
...
};
};
Das schlimmste Szenario aus beiden Welten
Einige von älteren Kerneln portierte Treiber enthalten Legacy-Verhalten im DT, das den schlechtesten Teil des Legacy-Schemas übernimmt und es dem neueren Schema aufzwingt, was die Sache einfacher machen soll. In solchen Treibern liest der Verbrauchertreiber die für die Suche zu verwendende Zeichenfolge mithilfe einer gerätespezifischen DT-Eigenschaft, der Lieferant verwendet eine andere lieferantenspezifische Eigenschaft, um den Namen zu definieren, der für die Registrierung der Lieferantenressource verwendet werden soll, und verwendet dann Verbraucher und Lieferant weiter das gleiche alte Schema, Strings zum Nachschlagen des Lieferanten zu verwenden. In diesem schlimmsten Szenario:
Der Touch-Treiber verwendet Code ähnlich dem folgenden Code:
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);
Das DT verwendet Code ähnlich dem folgenden:
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" }; };
Ändern Sie keine Framework-API-Fehler
Framework-APIs wie regulator
, clocks
, irq
, gpio
, phys
und extcon
geben -EPROBE_DEFER
als Fehlerrückgabewert zurück, um anzuzeigen, dass ein Gerät versucht, eine Prüfung durchzuführen, dies jedoch zu diesem Zeitpunkt nicht kann, und der Kernel sollte die Prüfung erneut versuchen später. Um sicherzustellen, dass .probe()
Funktion Ihres Geräts in solchen Fällen wie erwartet fehlschlägt, ersetzen Sie den Fehlerwert nicht und ordnen Sie ihn nicht neu zu. Das Ersetzen oder Neuzuordnen des Fehlerwerts kann dazu führen, dass -EPROBE_DEFER
gelöscht wird und Ihr Gerät nie überprüft wird.
Verwenden Sie devm_*() API-Varianten
Wenn das Gerät eine Ressource mithilfe einer devm_*()
-API abruft, wird die Ressource automatisch vom Kernel freigegeben, wenn die Prüfung durch das Gerät fehlschlägt oder die Prüfung erfolgreich ist und die Bindung später aufgehoben wird. Diese Funktionalität macht den Fehlerbehandlungscode in der Funktion probe()
sauberer, da keine goto
Sprünge erforderlich sind, um die von devm_*()
erfassten Ressourcen freizugeben, und vereinfacht die Treiberaufhebungsvorgänge.
Behandeln Sie die Aufhebung der Bindung des Gerätetreibers
Gehen Sie bei der Aufhebung der Bindung von Gerätetreibern bewusst vor und lassen Sie die Aufhebung der Bindung nicht undefiniert, da undefiniert nicht bedeutet, dass sie nicht zulässig ist. Sie müssen die Gerätetreiber-Aufhebung entweder vollständig implementieren oder die Gerätetreiber-Aufhebung explizit deaktivieren.
Implementierung der Gerätetreiber-Entbindung
Wenn Sie sich für die vollständige Implementierung der Gerätetreiber-Entbindung entscheiden, müssen Sie die Gerätetreiber sauber entbinden, um Speicher- oder Ressourcenlecks und Sicherheitsprobleme zu vermeiden. Sie können ein Gerät an einen Treiber binden, indem Sie die Funktion probe()
eines Treibers aufrufen, und die Bindung eines Geräts aufheben, indem Sie die Funktion remove()
des Treibers aufrufen. Wenn keine Funktion remove()
vorhanden ist, kann der Kernel das Gerät trotzdem entbinden; Der Treiberkern geht davon aus, dass der Treiber keine Aufräumarbeiten durchführen muss, wenn er die Verbindung zum Gerät aufhebt. Ein Treiber, der nicht an ein Gerät gebunden ist, muss keine expliziten Bereinigungsarbeiten durchführen, wenn beide der folgenden Bedingungen zutreffen:
Alle von der
probe()
Funktion eines Treibers erfassten Ressourcen erfolgen überdevm_*()
APIs.Das Hardwaregerät benötigt keine Abschalt- oder Stilllegungssequenz.
In dieser Situation übernimmt der Treiberkern die Freigabe aller über devm_*()
APIs erfassten Ressourcen. Wenn eine der vorhergehenden Aussagen nicht wahr ist, muss der Treiber eine Bereinigung durchführen (Ressourcen freigeben und die Hardware herunterfahren oder in den Ruhezustand versetzen), wenn er die Bindung zu einem Gerät aufhebt. Um sicherzustellen, dass ein Gerät ein Treibermodul sauber lösen kann, verwenden Sie eine der folgenden Optionen:
Wenn die Hardware keine Abschalt- oder Stilllegungssequenz benötigt, ändern Sie das Gerätemodul, um Ressourcen mithilfe
devm_*()
APIs abzurufen.Implementieren Sie die Treiberoperation
remove()
in derselben Struktur wie die Funktionprobe()
und führen Sie dann die Bereinigungsschritte mit der Funktionremove()
durch.
Explizites Deaktivieren der Gerätetreiber-Entbindung (nicht empfohlen)
Wenn Sie die Aufhebung der Bindung des Gerätetreibers explizit deaktivieren möchten, müssen Sie die Aufhebung der Bindung und das Entladen des Moduls verbieten.
Um das Aufheben der Bindung zu verhindern, setzen Sie das Flag
suppress_bind_attrs
“ in derstruct device_driver
des Treibers auf „true
“. Diese Einstellung verhindert, dass diebind
undunbind
Dateien imsysfs
Verzeichnis des Treibers angezeigt werden. Dieunbind
-Datei ermöglicht es dem Benutzerraum, die Entbindung eines Treibers von seinem Gerät auszulösen.Um das Entladen des Moduls zu verhindern, stellen Sie sicher, dass das Modul
[permanent]
inlsmod
hat. Wennmodule_exit()
odermodule_XXX_driver()
nicht verwendet werden, wird das Modul als[permanent]
markiert.
Laden Sie keine Firmware aus der Sondenfunktion heraus
Der Treiber sollte die Firmware nicht über die Funktion .probe()
laden, da er möglicherweise keinen Zugriff auf die Firmware hat, wenn der Treiber eine Prüfung durchführt, bevor das Flash- oder Permanentspeicher-basierte Dateisystem gemountet ist. In solchen Fällen blockiert die request_firmware*()
-API möglicherweise längere Zeit und schlägt dann fehl, was den Startvorgang unnötig verlangsamen kann. Verschieben Sie stattdessen das Laden der Firmware auf den Zeitpunkt, an dem ein Client das Gerät verwendet. Beispielsweise könnte ein Anzeigetreiber die Firmware laden, wenn das Anzeigegerät geöffnet wird.
Die Verwendung .probe()
zum Laden der Firmware kann in einigen Fällen in Ordnung sein, z. B. bei einem Uhrentreiber, der zum Funktionieren Firmware benötigt, das Gerät jedoch nicht dem Benutzerbereich zugänglich ist. Andere geeignete Anwendungsfälle sind möglich.
Implementieren Sie asynchrone Sondierungen
Unterstützen und verwenden Sie asynchrone Prüfungen, um zukünftige Verbesserungen zu nutzen, z. B. paralleles Laden von Modulen oder Geräteprüfungen zur Beschleunigung der Startzeit, die möglicherweise in zukünftigen Versionen zu Android hinzugefügt werden. Treibermodule, die keine asynchrone Prüfung verwenden, könnten die Wirksamkeit solcher Optimierungen verringern.
Um einen Treiber als Unterstützung und Bevorzugung asynchroner Prüfungen zu kennzeichnen, legen Sie das Feld probe_type
im struct device_driver
des Treibers fest. Das folgende Beispiel zeigt eine solche Unterstützung, die für einen Plattformtreiber aktiviert ist:
static struct platform_driver acme_driver = {
.probe = acme_probe,
...
.driver = {
.name = "acme",
...
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
Damit ein Treiber mit asynchroner Prüfung funktioniert, ist kein spezieller Code erforderlich. Beachten Sie jedoch Folgendes, wenn Sie Unterstützung für asynchrone Tests hinzufügen.
Machen Sie keine Annahmen über zuvor untersuchte Abhängigkeiten. Prüfen Sie direkt oder indirekt (die meisten Framework-Aufrufe) und geben Sie
-EPROBE_DEFER
zurück, wenn einer oder mehrere Lieferanten noch nicht bereit sind.Wenn Sie untergeordnete Geräte in der Prüffunktion eines übergeordneten Geräts hinzufügen, gehen Sie nicht davon aus, dass die untergeordneten Geräte sofort geprüft werden.
Wenn ein Test fehlschlägt, führen Sie eine ordnungsgemäße Fehlerbehandlung und Bereinigung durch (siehe Verwenden von devm_*()-API-Varianten ).
Verwenden Sie nicht MODULE_SOFTDEP, um Geräte-Probes zu bestellen
Die Funktion MODULE_SOFTDEP()
ist keine zuverlässige Lösung zur Gewährleistung der Reihenfolge von Gerätesonden und darf aus den folgenden Gründen nicht verwendet werden.
Aufgeschobene Untersuchung. Wenn ein Modul geladen wird, wird die Geräteprüfung möglicherweise verzögert, weil einer seiner Lieferanten nicht bereit ist. Dies kann zu einer Diskrepanz zwischen der Modulladereihenfolge und der Gerätesondenreihenfolge führen.
Ein Treiber, viele Geräte. Ein Treibermodul kann einen bestimmten Gerätetyp verwalten. Wenn das System mehr als eine Instanz eines Gerätetyps enthält und für diese Geräte jeweils eine andere Anforderung an die Sondenreihenfolge gilt, können Sie diese Anforderungen mithilfe der Modullastreihenfolge nicht berücksichtigen.
Asynchrone Sondierung. Treibermodule, die eine asynchrone Prüfung durchführen, prüfen ein Gerät nicht sofort, wenn das Modul geladen wird. Stattdessen übernimmt ein paralleler Thread die Geräteprüfung, was zu einer Nichtübereinstimmung zwischen der Modulladereihenfolge und der Geräteprüfungsreihenfolge führen kann. Wenn beispielsweise ein I2C-Haupttreibermodul eine asynchrone Abtastung durchführt und ein Touch-Treibermodul vom PMIC am I2C-Bus abhängig ist, wird möglicherweise zuvor versucht, den Touch-Treiber zu testen, selbst wenn der Touch-Treiber und der PMIC-Treiber in der richtigen Reihenfolge geladen werden die PMIC-Treibersonde.
Wenn Sie Treibermodule haben, die die Funktion MODULE_SOFTDEP()
verwenden, korrigieren Sie sie, damit sie diese Funktion nicht verwenden. Um Ihnen zu helfen, hat das Android-Team Änderungen vorinstalliert, die es dem Kernel ermöglichen, Bestellprobleme zu bewältigen, ohne MODULE_SOFTDEP()
zu verwenden. Insbesondere können Sie fw_devlink
verwenden, um die Prüfreihenfolge sicherzustellen und (nachdem alle Verbraucher eines Geräts geprüft haben) den sync_state()
Rückruf verwenden, um alle erforderlichen Aufgaben auszuführen.
Verwenden Sie für Konfigurationen #if IS_ENABLED() anstelle von #ifdef
Verwenden Sie #if IS_ENABLED(CONFIG_XXX)
anstelle von #ifdef CONFIG_XXX
, um sicherzustellen, dass der Code im #if
Block weiterhin kompiliert wird, wenn sich die Konfiguration in Zukunft in eine Tristate-Konfiguration ändert. Die Unterschiede sind wie folgt:
#if IS_ENABLED(CONFIG_XXX)
ergibt „true
, wennCONFIG_XXX
auf Modul (=m
) oder integriert (=y
) gesetzt ist.#ifdef CONFIG_XXX
wird alstrue
ausgewertet, wennCONFIG_XXX
auf „built-in“ (=y
) gesetzt ist, aber nicht, wennCONFIG_XXX
auf „module“ (=m
) gesetzt ist. Verwenden Sie dies nur, wenn Sie sicher sind, dass Sie dasselbe tun möchten, wenn die Konfiguration auf „Modul“ eingestellt oder deaktiviert ist.
Verwenden Sie für bedingte Kompilierungen das richtige Makro
Wenn ein CONFIG_XXX
auf Modul ( =m
) gesetzt ist, definiert das Build-System automatisch CONFIG_XXX_MODULE
. Wenn Ihr Treiber von CONFIG_XXX
gesteuert wird und Sie überprüfen möchten, ob Ihr Treiber als Modul kompiliert wird, verwenden Sie die folgenden Richtlinien:
Verwenden Sie in der C-Datei (oder einer anderen Quelldatei, die keine Header-Datei ist) für Ihren Treiber nicht
#ifdef CONFIG_XXX_MODULE
, da es unnötig restriktiv ist und kaputt geht, 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 Umfang dieser Datei. Um zu überprüfen, ob eine C-Datei (oder eine andere Quelldatei ohne Header) als Teil eines Moduls kompiliert wird, verwenden Sie daher#ifdef MODULE
(ohne das PräfixCONFIG_
).Bei Header-Dateien ist die gleiche Prüfung schwieriger, da Header-Dateien nicht direkt in eine Binärdatei, sondern als Teil einer C-Datei (oder anderer Quelldateien) kompiliert werden. Verwenden Sie die folgenden Regeln für Header-Dateien:
Bei einer Header-Datei, die
#ifdef MODULE
verwendet, ändert sich das Ergebnis je nachdem, welche Quelldatei es verwendet. Dies bedeutet, dass in derselben Headerdatei im selben Build verschiedene Teile des Codes für verschiedene Quelldateien kompiliert werden können (Modul im Vergleich zu integrierten oder deaktivierten Dateien). Dies kann nützlich sein, wenn Sie ein Makro definieren möchten, das für integrierten Code auf eine Art und für ein Modul auf eine andere Art und Weise erweitert werden muss.Für eine Header-Datei, die in einem Codestück kompiliert werden muss, wenn ein bestimmtes
CONFIG_XXX
auf Modul gesetzt ist (unabhängig davon, ob die Quelldatei, die es enthält, ein Modul ist), muss die Header-Datei#ifdef CONFIG_XXX_MODULE
verwenden.