Mit den folgenden Richtlinien können Sie die Robustheit und Zuverlässigkeit Ihrer Anbietermodule erhöhen. Wenn Sie viele Richtlinien einhalten, können Sie die richtige Modulladereihenfolge und die Reihenfolge, in der Treiber nach Geräten suchen müssen, leichter bestimmen.
Ein Modul kann eine Bibliothek oder ein Treiber sein.
Bibliotheksmodule sind Bibliotheken, die APIs für andere Module bereitstellen. Solche Module sind in der Regel 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. Es wird jedoch kein anderer Code ausgeführt, es sei denn, er wird von einem externen Modul ausgelöst.Treibermodule sind Treiber, die nach einem bestimmten Gerätetyp suchen oder eine Bindung an ihn vornehmen. Solche Module sind hardwarespezifisch. Beispiele für Treibermodule sind UART-, PCIe- und Video-Encoder-Hardware. Treibermodule werden nur aktiviert, wenn sich das zugehörige Gerät im System befindet.
Wenn das Gerät nicht vorhanden ist, wird nur der
module_init()
-Code ausgeführt, der den Treiber beim Driver Core Framework registriert.Wenn das Gerät vorhanden ist und der Treiber erfolgreich nach diesem Gerät sucht oder eine Bindung daran herstellt, wird möglicherweise anderer Modulcode ausgeführt.
Modul-init und -exit richtig verwenden
Treibermodule müssen einen Treiber in module_init()
registrieren und einen Treiber in module_exit()
abmelden. Eine Möglichkeit, diese Einschränkungen durchzusetzen, besteht darin, Wrapper-Makros zu verwenden. Dadurch 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()
.
Einige Treibermodule verwenden module_init()
und module_exit()
, da sie mehrere Treiber registrieren. Wenn in einem Treibermodul mehrere Treiber mit module_init()
und module_exit()
registriert werden, versuchen Sie, die Treiber in einem einzigen Treiber zu kombinieren. Sie können beispielsweise anhand des compatible
-Strings oder der Hilfsdaten 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.
Makro „MODULE_DEVICE_TABLE“ verwenden
Treibermodule müssen das Makro MODULE_DEVICE_TABLE
enthalten, mit dem 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 Lademodule für Geräte zu vermeiden, die im System nicht vorhanden sind. Beispiele zur Verwendung des Makros finden Sie im Upstream-Code.
CRC-Abweichungen aufgrund von vordefinierten Datentypen vermeiden
Fügen Sie keine Headerdateien ein, um Informationen zu vordefinierten Datentypen zu erhalten.
Einige Structs, Unions und andere Datentypen, die in einer Headerdatei (header-A.h
) definiert sind, können in einer anderen Headerdatei (header-B.h
) weitergeleitet werden, die normalerweise Verweise auf diese Datentypen verwendet. Dieses Codemuster bedeutet, dass der Kernel beabsichtigt, die Datenstruktur für die Nutzer von header-B.h
privat zu halten.
Nutzer von header-B.h
sollten header-A.h
nicht einschließen, um direkt auf die internen Elemente dieser vordefinierten Datenstrukturen zuzugreifen. Dies führt zu CONFIG_MODVERSIONS
-CRC-Nichtübereinstimmungsproblemen (die zu ABI-Compliance-Problemen führen), wenn ein anderer Kernel (z. B. der GKI-Kernel) versucht, das Modul zu laden.
Beispiel: struct fwnode_handle
ist in include/linux/fwnode.h
definiert, wird aber in include/linux/device.h
als struct fwnode_handle;
vordeklariert, weil der Kernel versucht, die Details von struct fwnode_handle
vor den Nutzern von include/linux/device.h
geheim zu halten. Fügen Sie in diesem Szenario einem Modul #include <linux/fwnode.h>
nicht hinzu, um Zugriff auf Mitglieder von struct fwnode_handle
zu erhalten. Jedes Design, in dem Sie solche Headerdateien einfügen müssen, ist ein schlechtes Designmuster.
Nicht direkt auf Kernkernelstrukturen zugreifen
Der direkte Zugriff auf oder das Ändern von Kern-Kernel-Datenstrukturen kann zu unerwünschtem Verhalten führen, einschließlich Speicherlecks, Abstürzen und schlechter Kompatibilität mit zukünftigen Kernel-Releases. Eine Datenstruktur ist eine Kerndatenstruktur, wenn sie eine der folgenden Bedingungen erfüllt:
Die Datenstruktur ist unter
KERNEL-DIR/include/
definiert. Beispiel:struct device
undstruct dev_links_info
. Ininclude/linux/soc
definierte Datenstrukturen sind ausgenommen.Die Datenstruktur wird vom Modul zugewiesen oder initialisiert, aber für den Kernel sichtbar gemacht, indem sie indirekt (über einen Pointer in einem struct) oder direkt als Eingabe in eine vom Kernel exportierte Funktion übergeben wird. Beispielsweise initialisiert ein
cpufreq
-Treibermodul denstruct cpufreq_driver
und gibt ihn dann als Eingabe ancpufreq_register_driver()
weiter. Danach sollte dascpufreq
-Treibermodulstruct cpufreq_driver
nicht mehr direkt ändern, dastruct cpufreq_driver
durch den Aufruf voncpufreq_register_driver()
für den Kernel sichtbar wird.Die Datenstruktur wird von Ihrem Modul nicht initialisiert. Beispiel:
struct regulator_dev
, das vonregulator_register()
zurückgegeben wird.
Greifen Sie nur über vom Kernel exportierte Funktionen oder über Parameter, die explizit als Eingabe an Anbieter-Hooks übergeben werden, auf Kernel-Datenstrukturen zu. Wenn Sie keine API oder keinen Anbieter-Hook haben, um Teile der Kerndatenstruktur des Kernels 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 zum Ändern von
device.devres_head
einedevm_*()
-Funktion wiedevm_clk_get()
,devm_regulator_get()
oderdevm_kzalloc()
.Wenn Sie Felder in
struct device.links
ändern möchten, verwenden Sie eine Device Link API wiedevice_link_add()
oderdevice_link_del()
.
Devicetree-Knoten mit kompatibler Property nicht parsen
Wenn ein Gerätebaumknoten (DT) das Attribut compatible
hat, wird ihm automatisch ein struct device
zugewiesen oder wenn of_platform_populate()
auf dem übergeordneten DT-Knoten aufgerufen wird (in der Regel vom Gerätetreiber des übergeordneten Geräts). Standardmäßig (außer bei einigen Geräten, die früh für den Scheduler initialisiert werden) wird davon ausgegangen, dass ein DT-Knoten mit einer compatible
-Eigenschaft eine struct device
und einen entsprechenden Gerätetreiber hat. Alle anderen Ausnahmen werden bereits vom vorgelagerten Code verarbeitet.
Außerdem betrachtet fw_devlink
(früher of_devlink
) DT-Knoten mit dem Attribut compatible
als Geräte mit einem zugewiesenen struct device
, der von einem Treiber geprüft wird. Wenn ein DT-Knoten eine compatible
-Eigenschaft hat, die zugewiesene struct device
aber nicht gescannt wird, kann fw_devlink
die Suche seiner Verbrauchergeräte blockieren oder sync_state()
-Aufrufe für seine Geräte des Lieferanten 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 mit einer compatible
-Eigenschaft zu finden und dann diesen DT-Knoten zu parsen, beheben Sie das Problem, indem Sie einen Gerätetreiber schreiben, der das Gerät prüfen kann, oder entfernen Sie die compatible
-Eigenschaft (nur möglich, wenn sie nicht upstreamed wurde). Wenn Sie Alternativen besprechen möchten, wenden Sie sich an das Android-Kernel-Team unter kernel-team@android.com. Sie müssen Ihre Anwendungsfälle begründen können.
Mit DT-phandles nach Lieferanten suchen
Verweise auf einen Lieferanten sollten nach Möglichkeit mit einem Phandle (eine Referenz oder ein Verweis auf einen DT-Knoten) in DT erfolgen. Wenn Sie Standard-DT-Bindungen und -Phandles verwenden, um auf Lieferanten zu verweisen, kann fw_devlink
(früher of_devlink
) automatisch Geräteabhängigkeiten ermitteln, indem die DT zur Laufzeit geparst wird. Der Kernel kann dann automatisch Geräte in der richtigen Reihenfolge prüfen, sodass keine Modulladereihenfolge oder MODULE_SOFTDEP()
erforderlich ist.
Legacy-Szenario (keine DT-Unterstützung im ARM-Kernel)
Bevor die DT-Unterstützung den ARM-Kerneln hinzugefügt wurde, suchten Verbraucher wie Touchgeräte nach Anbietern wie z. B. Aufsichtsbehörden mithilfe global eindeutiger Strings.
Der ACME PMIC-Treiber könnte beispielsweise mehrere Regulierungsbehörden registrieren oder bewerben (z. B. acme-pmic-ldo1
bis acme-pmic-ldo10
) und ein Touch-Treiber könnte mithilfe von regulator_get(dev, "acme-pmic-ldo10")
nach einer Regulierungsbehörde suchen.
Auf einer anderen Platine kann der LDO8 jedoch das Touch-Gerät liefern und so ein umständliches System erzeugen, in dem derselbe Touch-Treiber für jedes Board, in dem das Touch-Gerät verwendet wird, den richtigen Suchstring für den Regler ermitteln muss.
Aktuelles Szenario (DT-Unterstützung im ARM-Kernel)
Nachdem ARM-Kerneln die DT-Unterstützung hinzugefügt wurde, können Verbraucher Lieferanten im DT anhand eines phandles identifizieren, der auf den Gerätebaumknoten des Lieferanten verweist.
Verbraucher können die Ressource auch nach ihrer Verwendung benennen, anstatt nach dem Lieferanten. 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 den Sensor des Touch-Geräts versorgen. Die zugehörige Datenstruktur 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 schlechteste Szenario
Einige aus älteren Kerneln portierte Treiber enthalten im DT altes Verhalten, das den schlechtesten Teil des alten Schemes übernimmt und auf das neuere Scheme überträgt, das eigentlich die Dinge vereinfachen soll. Bei solchen Treibern liest der Verbrauchertreiber den String, der für die Suche verwendet werden soll, 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. Anschließend verwenden der Verbraucher und der Lieferant weiterhin dasselbe alte Schema, um den Lieferanten mithilfe von Strings zu suchen. 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);
Der DT verwendet Code, der in etwa so aussieht:
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 Fehlerrückgabewert zurück, um anzugeben, dass ein Gerät eine Prüfung versucht, diese aber derzeit nicht durchführen kann. Der Kernel sollte die Prüfung später noch einmal versuchen. Damit die .probe()
-Funktion Ihres Geräts in diesen Fällen nicht wie erwartet funktioniert, sollten Sie den Fehlerwert nicht ersetzen oder neu zuordnen.
Wenn Sie den Fehlerwert ersetzen oder neu zuordnen, wird -EPROBE_DEFER
möglicherweise gelöscht und Ihr Gerät wird nie geprüft.
devm_*() API-Varianten verwenden
Wenn das Gerät eine Ressource über eine devm_*()
API abruft, wird die Ressource vom Kernel automatisch freigegeben, wenn die Prüfung des Geräts fehlschlägt oder erfolgreich ist und später aufgehoben wird. Durch diese Funktion wird der Fehlerbehandlungscode in der Funktion probe()
übersichtlicher, da keine goto
-Sprünge notwendig sind, um die von devm_*()
übernommenen Ressourcen freizugeben. Dies vereinfacht Vorgänge zum Aufheben der Treiberbindung.
Aufheben der Gerätetreiberbindung verarbeiten
Entfernen Sie Gerätetreiber überlegt und lassen Sie das Aufheben der Bindung nicht undefiniert, da „nicht definiert“ nicht bedeutet, dass die Bindung nicht zulässig ist. Sie müssen das Aufheben der Bindung für Gerätetreiber entweder vollständig implementieren oder das Aufheben der Bindung für Gerätetreiber explizit deaktivieren.
Aufheben der Bindung für Gerätetreiber implementieren
Wenn Sie die Aufhebung der Bindung von Gerätetreibern vollständig implementieren, müssen Sie die Bindung der Gerätetreiber sauber aufheben, 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 die Bindung eines Geräts aufheben, indem Sie die remove()
-Funktion des Treibers aufrufen. Wenn keine remove()
-Funktion vorhanden ist, kann der Kernel das Gerät trotzdem entkoppeln. Der Treiberkern geht davon aus, dass beim Entkoppeln vom Gerät keine Bereinigungsarbeiten durch den Treiber erforderlich sind. Bei einem Treiber, der nicht mit einem Gerät verknüpft ist, müssen keine expliziten Bereinigungsarbeiten durchgeführt werden, wenn folgende Bedingungen erfüllt sind:
Alle Ressourcen, die über die
probe()
-Funktion eines Treibers abgerufen werden, werden überdevm_*()
-APIs abgerufen.Für das Hardwaregerät ist keine Abschalt- oder Ruhesequenz erforderlich.
In diesem Fall wird die Freigabe aller über devm_*()
APIs erworbenen Ressourcen vom Treiberkern verwaltet. Wenn eine der vorherigen Anweisungen nicht zutrifft, muss der Treiber beim Aufheben der Bindung an ein Gerät eine Bereinigung ausführen (Ressourcen freigeben und die Hardware herunterfahren oder stilllegen). Verwenden Sie eine der folgenden Optionen, damit ein Gerät ein Treibermodul sauber entkoppeln kann:
Wenn die Hardware keine Abschaltungs- oder Stilllegungssequenz benötigt, ändern Sie das Gerätemodul, um Ressourcen mit
devm_*()
APIs zu beziehen.Implementieren Sie den
remove()
-Treibervorgang im selben Namespace wie dieprobe()
-Funktion und führen Sie dann die Schritte zur Bereinigung mit derremove()
-Funktion aus.
Aufheben der Bindung von Gerätetreibern explizit deaktivieren (nicht empfohlen)
Wenn Sie die Aufhebung der Bindung von Gerätetreibern explizit deaktivieren möchten, müssen Sie die Aufhebung der Bindung und das Entladen von Modulen deaktivieren.
Wenn Sie die Aufhebung der Bindung nicht zulassen möchten, setzen Sie das
suppress_bind_attrs
-Flag imstruct device_driver
des Treibers auftrue
. Dadurch wird verhindert, dass die Dateienbind
undunbind
imsysfs
-Verzeichnis des Treibers angezeigt werden. Über die Dateiunbind
kann der Nutzerbereich die Bindung eines Treibers von seinem Gerät aufheben.Um zu verhindern, dass das Modul entladen wird, muss
[permanent]
auflsmod
vorhanden sein. Wenn Sie wedermodule_exit()
nochmodule_XXX_driver()
verwenden, wird das Modul als[permanent]
gekennzeichnet.
Firmware nicht über die Prüffunktion laden
Der Treiber sollte die Firmware nicht über die .probe()
-Funktion laden, da er möglicherweise keinen Zugriff auf die Firmware hat, wenn er vor dem Bereitstellen des Flash- oder permanenten Speichers basierten Dateisystems prüft. In solchen Fällen kann die request_firmware*()
API lange blockiert werden und dann fehlschlagen, was den Bootvorgang unnötig verlangsamt. Verzögern Sie das Laden der Firmware stattdessen so lange, bis ein Client das Gerät verwendet. So könnte ein Displaytreiber beispielsweise die Firmware laden, wenn das Displaygerät geöffnet wird.
In einigen Fällen ist es in Ordnung, Firmware über .probe()
zu laden, z. B. bei einem Taktgebertreiber, der Firmware zum Funktionieren benötigt, das Gerät aber nicht für den Nutzerbereich freigegeben ist. Andere geeignete Anwendungsfälle sind möglich.
Asynchrones Probieren implementieren
Unterstützen und verwenden Sie asynchrones Probieren, um von zukünftigen Verbesserungen zu profitieren, z. B. paralleles Modulladen oder Geräteprüfung, um die Bootzeit zu verkürzen, die in zukünftigen Android-Releases hinzugefügt werden könnten. Treibermodule, die keine asynchronen Prüfungen verwenden, können die Effektivität solcher Optimierungen verringern.
Wenn Sie einen Treiber als unterstützend und bevorzugt für die asynchrone Suche kennzeichnen möchten, legen Sie das Feld probe_type
im Mitglied struct device_driver
des Treibers fest. Im folgenden Beispiel ist diese Unterstützung für einen Plattformtreiber aktiviert:
static struct platform_driver acme_driver = {
.probe = acme_probe,
...
.driver = {
.name = "acme",
...
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
Für die Verwendung eines Treibers mit asynchronem Probieren ist kein spezieller Code erforderlich. Beachten Sie jedoch Folgendes, wenn Sie die Unterstützung für asynchrone Prüfungen hinzufügen.
Gehen Sie nicht davon aus, dass zuvor geprüfte Abhängigkeiten noch vorhanden sind. Prüfen Sie direkt oder indirekt (die meisten Framework-Aufrufe) und geben Sie
-EPROBE_DEFER
zurück, wenn ein oder mehrere Anbieter noch nicht bereit sind.Wenn Sie untergeordnete Geräte 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 devm_*() API-Varianten verwenden).
Verwenden Sie MODULE_SOFTDEP nicht, um Geräteprüfungen anzuordnen.
Die Funktion MODULE_SOFTDEP()
ist keine zuverlässige Lösung, um die Reihenfolge der Geräteprüfungen zu garantieren, und darf aus den folgenden Gründen nicht verwendet werden.
Verzögerte Prüfung: Wenn ein Modul geladen wird, wird die Geräteprüfung möglicherweise verschoben, weil einer der Lieferanten noch nicht bereit ist. Dies kann zu einer Abweichung zwischen der Modulladereihenfolge und der Geräteprüfreihenfolge 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 diese Geräte jeweils eine andere Probenreihenfolge erfordern, können Sie diese Anforderungen nicht mit der Modulladereihenfolge erfüllen.
Asynchrones Probieren Treibermodule, die asynchrone Prüfungen durchführen, prüfen ein Gerät nicht sofort, wenn das Modul geladen wird. Stattdessen wird die Geräteprüfung von einem parallelen Thread ausgeführt, was zu einer Abweichung zwischen der Modulladereihenfolge und der Geräteprüfreihenfolge führen kann. Wenn beispielsweise ein I2C-Haupttreibermodul asynchrone Prüfungen durchführt und ein Touch-Treibermodul vom PMIC auf dem I2C-Bus abhängt, wird der Test des Touch-Treibers möglicherweise vor dem Test des PMIC-Treibers ausgeführt, auch wenn der Touch-Treiber und der PMIC-Treiber in der richtigen Reihenfolge geladen werden.
Wenn Sie Treibermodule haben, die die Funktion MODULE_SOFTDEP()
verwenden, beheben Sie das Problem. Zu Ihrer Unterstützung hat das Android-Team Änderungen vorgelagert, durch die der Kernel ohne MODULE_SOFTDEP()
Probleme beim Bestellen lösen kann. Genauer gesagt können Sie fw_devlink
verwenden, um die Reihenfolge der Prüfungen sicherzustellen. Nachdem alle Nutzer eines Geräts geprüft haben, können Sie den sync_state()
-Callback verwenden, um alle erforderlichen Aufgaben auszufü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 auch dann weiter kompiliert wird, wenn die Konfiguration in Zukunft in eine Tristate-Konfiguration geändert wird. Es bestehen jedoch folgende Unterschiede:
#if IS_ENABLED(CONFIG_XXX)
wird mittrue
ausgewertet, wennCONFIG_XXX
auf „Modul“ (=m
) oder „Integriert“ (=y
) gesetzt ist.#ifdef CONFIG_XXX
ergibttrue
, wennCONFIG_XXX
auf „integriert“ (=y
) gesetzt ist, nicht aber, wennCONFIG_XXX
auf Modul (=m
) gesetzt ist. Verwenden Sie diese Option nur, wenn Sie sicher sind, dass Sie die gleiche Aktion ausführen möchten, wenn die Konfiguration auf Modul festgelegt oder deaktiviert ist.
Das richtige Makro für bedingte Kompilierungen verwenden
Wenn CONFIG_XXX
auf Modul (=m
) gesetzt ist, definiert das Build-System CONFIG_XXX_MODULE
automatisch. Wenn Ihr Treiber von CONFIG_XXX
gesteuert wird und Sie prüfen möchten, ob er als Modul kompiliert wird, beachten Sie die folgenden 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 einschränkend ist und zu Fehlern führt, wenn die Konfiguration inCONFIG_XYZ
umbenannt wird. Für jede Quelldatei ohne Header, 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 Quelldatei ohne Header) als Teil eines Moduls kompiliert wird, verwenden Sie#ifdef MODULE
(ohne das PräfixCONFIG_
).Bei Headerdateien ist diese Prüfung schwieriger, da Headerdateien nicht direkt in eine Binärdatei kompiliert, sondern als Teil einer C-Datei (oder anderer Quelldateien) kompiliert werden. Beachten Sie die folgenden Regeln für Headerdateien:
Bei einer Headerdatei, in der
#ifdef MODULE
verwendet wird, hängt das Ergebnis davon ab, in welcher Quelldatei es verwendet wird. Das bedeutet, dass für dieselbe Headerdatei im selben Build unterschiedliche Codeteile für verschiedene Quelldateien kompiliert werden können (Modul im Vergleich zu „eingebaut“ oder „deaktiviert“). Dies kann nützlich sein, wenn Sie ein Makro definieren möchten, das in eine Richtung für integrierten Code und auf eine andere Weise für ein Modul erweitert werden muss.Für eine Headerdatei, die einen Code kompilieren muss, wenn eine bestimmte
CONFIG_XXX
auf „Modul“ gesetzt ist (unabhängig davon, ob die Quelldatei, in der sie enthalten ist, ein Modul ist), muss die Headerdatei#ifdef CONFIG_XXX_MODULE
verwenden.