הנחיות למודול הספק

כדי לשפר את העמידות והאמינות של המודולים של הספקים, כדאי להשתמש בהנחיות הבאות. יש הרבה הנחיות שבעזרתן קל יותר לקבוע את סדר הטעינה הנכון של המודולים ואת הסדר שבו מנהלי ההתקנים צריכים לחפש מכשירים.

מודול יכול להיות ספרייה או מנהל התקן.

  • מודולים של ספריות הן ספריות שמספקות ממשקי API לשימוש במודולים אחרים. בדרך כלל, המודולים האלה לא ספציפיים לחומרה. דוגמאות למודולים של ספריות כוללות מודול של הצפנה מסוג AES, ה-framework של remoteproc שעבר הידור כמודול ומודול יומן. קוד המודול ב-module_init() פועל כדי להגדיר מבני נתונים, אבל אף קוד אחר לא פועל אלא אם הוא מופעל על ידי מודול חיצוני.

  • מודולים של מנהלי התקנים הם מנהלי התקנים שמאתרים מכשיר מסוג ספציפי או מתחברים אליו. מודולים כאלה הם ספציפיים לחומרה. דוגמאות למודולים של מנהלי התקנים: UART, PCIe וחומרה של מקודד וידאו. המודולים של מנהלי התקנים מופעלים רק כשהמכשיר המשויך אליהם נמצא במערכת.

    • אם המכשיר לא נמצא, קוד המודול היחיד שפועל הוא הקוד module_init() שמרשם את מנהל ההתקן במסגרת הליבה של מנהל ההתקן.

    • אם המכשיר נמצא והדרייבר מצליח לחפש את המכשיר או לקשר אליו, יכול להיות שקוד אחר של המודול יפעל.

שימוש נכון ב-init וב-exit של המודול

מודולים של מנהלי התקנים חייבים לרשום מנהל התקנים ב-module_init() ולבטל את הרישום של מנהל התקנים ב-module_exit(). אחת הדרכים לאכוף את ההגבלות האלה היא להשתמש בפקודות מאקרו של wrapper, כדי להימנע משימוש ישיר בפקודות המאקרו module_init(), *_initcall() או module_exit().

  • למודולים שאפשר לפרוק, משתמשים ב-module_subsystem_driver(). דוגמאות: module_platform_driver(), module_i2c_driver() ו-module_pci_driver().

  • במודולים שלא ניתן לפרוק, משתמשים ב-builtin_subsystem_driver(). דוגמאות: builtin_platform_driver(),‏ builtin_i2c_driver() ו-builtin_pci_driver().

בחלק מהמודולים של מנהלי התקנים משתמשים ב-module_init() וב-module_exit() כי הם רושמים יותר מנהג אחד. במודול של מנהל התקן שמשתמש ב-module_init() וב-module_exit() כדי לרשום כמה מנהלי התקנים, נסו לשלב את מנהלי ההתקנים למנהל התקן אחד. לדוגמה, אפשר להבדיל באמצעות המחרוזת compatible או נתוני ה-aux של המכשיר במקום לרשום מנהלי התקנים נפרדים. לחלופין, אפשר לפצל את מודול הנהג לשני מודולים.

חריגות של פונקציות init ו-exit

מודולים של ספריות לא רושמים מנהלי התקנים והם פטורים מההגבלות על module_init() ו-module_exit(), כי יכול להיות שהם יצטרכו את הפונקציות האלה כדי להגדיר מבני נתונים, תורים של משימות או חוטי ליבה.

שימוש במאקרו MODULE_DEVICE_TABLE

מודולי מנהלי ההתקנים חייבים לכלול את המאקרו MODULE_DEVICE_TABLE, שמאפשר למרחב המשתמש לקבוע את המכשירים שנתמכים על ידי מודול מנהל ההתקנים לפני טעינת המודול. מערכת Android יכולה להשתמש בנתונים האלה כדי לבצע אופטימיזציה של טעינת המודולים, למשל כדי למנוע טעינת מודולים למכשירים שלא נמצאים במערכת. דוגמאות לשימוש במאקרו מפורטות בקוד של המקורות העליונים.

הימנעות מאי-התאמות CRC עקב סוגי נתונים שהוגדרו מראש

לא כוללים קובצי כותרות כדי לקבל גישה לסוגים של נתונים שהוצהרו מראש. אפשר להצהיר מראש על מבנים מסוימים, על יוניונים ועל סוגי נתונים אחרים שהוגדרו בקובץ כותרת (header-A.h) בקובץ כותרת אחר (header-B.h) שמשתמש בדרך כלל בצבעים למעקב אחרי סוגי הנתונים האלה. דפוס הקוד הזה פירושו שהליבה מנסה באופן מכוון לשמור על מבנה הנתונים פרטי למשתמשים של header-B.h.

אסור למשתמשים של header-B.h לכלול header-A.h כדי לגשת ישירות לגורמים הפנימיים של מבני הנתונים האלה שהוצהרו להעברה. הפעולה הזו גורמת לבעיות של אי-התאמה ב-CRC של CONFIG_MODVERSIONS (שגורמות לבעיות תאימות ל-ABI) כשליבה אחרת (כמו הליבה של GKI) מנסה לטעון את המודול.

לדוגמה, struct fwnode_handle מוגדר ב-include/linux/fwnode.h, אבל מוצהר קדימה כ-struct fwnode_handle; ב-include/linux/device.h כי הליבה מנסה לשמור על הפרטיות של הפרטים של struct fwnode_handle מהמשתמשים של include/linux/device.h. בתרחיש הזה, לא מוסיפים את #include <linux/fwnode.h> למודול כדי לקבל גישה לחברים של struct fwnode_handle. כל עיצוב שבו צריך לכלול קבצים כאלה של כותרות הוא סימן לדפוס עיצוב שגוי.

אין לגשת ישירות למבנים של הליבה של הליבה

גישה ישירה למבנים של נתוני הליבה או שינוי שלהם עלולה להוביל להתנהגות לא רצויה, כולל דליפות זיכרון, קריסות ופגיעה בתאימות לגרסאות עתידיות של הליבה. מבנה נתונים הוא מבנה נתונים של ליבה אם הוא עומד באחד מהתנאים הבאים:

  • מבנה הנתונים מוגדר בקטע KERNEL-DIR/include/. לדוגמה, struct device ו-struct dev_links_info. מבני נתונים שמוגדרים ב-include/linux/soc פטורים.

  • מבנה הנתונים מוקצה או מופעל על ידי המודול, אבל הוא מוצג לליבה על ידי העברה, באופן עקיף (דרך מצביע ב-struct) או באופן ישיר, כקלט בפונקציה שיוצאת מהליבה. לדוגמה, מודול מנהל ההתקן cpufreq מפעיל את struct cpufreq_driver ולאחר מכן מעביר אותו כקלט אל cpufreq_register_driver(). אחרי הנקודה הזו, מודול הנהג cpufreq לא אמור לשנות את struct cpufreq_driver ישירות, כי קריאה ל-cpufreq_register_driver() הופכת את struct cpufreq_driver גלוי לליבה.

  • מבנה הנתונים לא מאותחל על ידי המודול. לדוגמה, הערך struct regulator_dev שהוחזר על ידי regulator_register().

גישה למבנים של נתוני הליבה רק דרך פונקציות שיוצאו על ידי הליבה או דרך פרמטרים שהועברו במפורש כקלט ל-hooks של ספקים. אם אין לכם API או ווקשן של ספק כדי לשנות חלקים ממבנה הנתונים של הליבה, סביר להניח שזה מכוון ואין לשנות את מבנה הנתונים מהמודולים. לדוגמה, אין לשנות שדות בתוך struct device או struct device.links.

  • כדי לשנות את device.devres_head, משתמשים בפונקציה devm_*() כמו devm_clk_get(),‏ devm_regulator_get() או devm_kzalloc().

  • כדי לשנות שדות ב-struct device.links, צריך להשתמש ב-API של קישור המכשיר, כמו device_link_add() או device_link_del().

אין לנתח צומתי devicetree עם נכס תואם

אם לצומת של עץ מכשיר (DT) יש מאפיין compatible, המערכת מקצה לו struct device באופן אוטומטי או כשמתבצעת קריאה ל-of_platform_populate() בצומת האב של DT (בדרך כלל על ידי מנהל ההתקן של המכשיר ההורה). ברירת המחדל (למעט מכשירים מסוימים שהוגדרו מראש לתזמון) היא שלצומת DT עם מאפיין compatible יש struct device ומנהל התקן תואם למכשיר. כל החריגים האחרים כבר מטופלים על ידי קוד ה-upstream.

בנוסף, fw_devlink (לשעבר of_devlink) מתייחס לצמתים של DT עם המאפיין compatible כמכשירים עם struct device שהוקצה להם ונבדק על ידי מנהל התקן. אם לצומת DT יש מאפיין compatible אבל לא מתבצע בדיקה של struct device שהוקצה, fw_devlink יכול לחסום את הבדיקה של מכשירי הצרכנים שלו או לחסום את הקריאה לשיחות sync_state() במכשירי הספק שלו.

אם הנהג משתמש בפונקציה of_find_*() (כמו of_find_node_by_name() או of_find_compatible_node()) כדי למצוא באופן ישיר צומת DT שיש לו מאפיין compatible, ולאחר מכן מנתח את צומת ה-DT הזה, אפשר לתקן את המודול על ידי כתיבת מנהל התקן שיכול לבדוק את המכשיר או להסיר את המאפיין compatible (אפשרי רק אם הוא לא בוצע ב-upstream). כדי לדון בחלופות, תוכלו לפנות לצוות הליבה של Android בכתובת kernel-team@android.com ולהתכונן להצדיק את תרחישי השימוש שלכם.

שימוש ב-phandles של DT לחיפוש ספקים

כשהדבר אפשרי, מומלץ להפנות לספק באמצעות phandle (הפניה או מצביע לצומת DT) ב-DT. שימוש בקישוריות רגילות של DT וב-phandles כדי להפנות לספקים מאפשר ל-fw_devlink (לשעבר of_devlink) לקבוע באופן אוטומטי יחסי תלות בין מכשירים על ידי ניתוח ה-DT בזמן הריצה. לאחר מכן, הליבה יכולה לבדוק את המכשירים באופן אוטומטי בסדר הנכון, וכך לבטל את הצורך בסדר טעינה של מודולים או ב-MODULE_SOFTDEP().

תרחיש מדור קודם (אין תמיכה ב-DT בליבה של ARM)

בעבר, לפני שתמיכה ב-DT נוספה לליבת ARM, צרכנים כמו מכשירי מגע חיפשו ספקים כמו רגולטורים באמצעות מחרוזות ייחודיות ברחבי העולם. לדוגמה, מנהל ה-PMIC של ACME יכול לרשום או לפרסם מספר רגולטורים (כמו acme-pmic-ldo1 עד acme-pmic-ldo10), ומנהל מגע יכול לחפש רגולטור באמצעות regulator_get(dev, "acme-pmic-ldo10"). עם זאת, בלוח אחר, ה-LDO8 עשוי לספק את מכשיר המגע, וכתוצאה מכך נוצרת מערכת מסורבלת שבה אותו מנהל מגע צריך לקבוע את המחרוזת הנכונה לחיפוש של הרגולטור בכל לוח שבו משתמש מכשיר המגע.

התרחיש הנוכחי (תמיכה ב-DT בליבה (kernel) של ARM)

אחרי שנוספה תמיכה ב-DT לליבות של ARM, הצרכנים יכולים לזהות ספקים ב-DT על ידי הפניה לצומת עץ המכשיר של הספק באמצעות ידית. הצרכנים יכולים גם לתת שם למשאב על סמך המטרה שלו, במקום על סמך מי שספק אותו. לדוגמה, מנהל המגע מהדוגמה הקודמת יכול להשתמש ב-regulator_get(dev, "core") וב-regulator_get(dev, "sensor") כדי לקבל את הספקים שמפעילים את הליבה והחיישן של מכשיר המגע. ה-DT המשויך למכשיר כזה דומה לדוגמת הקוד הבאה:

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 {
        ...
    };
};

תרחיש הכי גרוע

חלק מהדריברים שהועברו מליבת ליבה ישנות יותר כוללים התנהגות מדור קודם ב-DT, שמשתמשת בחלק הגרוע ביותר מהתוכנית הקודמת ומאלצת אותה על התוכנית החדשה שנועדה להקל על הדברים. בדרייברים כאלה, מנהל ההתקן של הצרכן קורא את המחרוזת לשימוש בחיפוש באמצעות מאפיין DT ספציפי למכשיר, הספק משתמש במאפיין אחר ספציפי לספק כדי להגדיר את השם שישמש לרישום המשאב של הספק, ואז הצרכן והספק ממשיכים להשתמש באותה סכימה ישנה של שימוש במחרוזות כדי לחפש את הספק. בתרחיש הזה, שמשלב את הכי גרוע משני העולמות:

  • מנהל ההתקן של המגע משתמש בקוד דומה לקוד הבא:

    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 נעשה שימוש בקוד דומה לזה:

    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"
      };
    };
    

לא לשנות שגיאות API של מסגרת

ממשקי API, כגון regulator, clocks, irq, gpio, phys, ו-extcon, מחזיר את -EPROBE_DEFER כ-prottime בשלב מאוחר יותר. כדי להבטיח שהפונקציה .probe() של המכשיר תיכשל כצפוי במקרים כאלה, לא כדאי להחליף או למפות מחדש את ערך השגיאה. החלפה או מיפוי מחדש של ערך השגיאה עלולים לגרום לכך שהמכשיר לא ייבדק אף פעם.-EPROBE_DEFER

שימוש בגרסאות API של devm_*()

כשהמכשיר מקבל משאב באמצעות API של devm_*(), המשאב משוחרר באופן אוטומטי על ידי הליבה אם המכשיר נכשל בבדיקה, או מבצע בדיקה ומאוחר יותר לא קשור. היכולת הזו מאפשרת לנקות את קוד הטיפול בשגיאות בפונקציה probe(), כי היא לא מחייבת קפיצות של goto לשחרור המשאבים שנצברו על ידי devm_*() ומפשטת את פעולות ביטול הקישור של מנהלי ההתקנים.

טיפול בביטול הקישור של הנהג למכשיר

חשוב לבטל את הקישור של מנהלי ההתקנים באופן מכוון ולא להשאיר את ביטול הקישור לא מוגדר, כי לא מוגדר לא מעיד על איסור. צריך להטמיע באופן מלא את ביטול הקישור של מנהל ההתקן למכשיר או להשבית באופן מפורש את ביטול הקישור של מנהל ההתקן למכשיר.

הטמעת ביטול קישור של מנהל התקן למכשיר

כשבוחרים להטמיע באופן מלא את ביטול הקישור בין מנהלי ההתקנים של המכשיר, צריך לבטל את הקישור של מנהלי ההתקנים של המכשירים בצורה מדויקת כדי למנוע דליפות זיכרון או משאבים ובעיות אבטחה. כדי לקשר מכשיר לנהג, קוראים לפונקציה probe() של הנהג. כדי לבטל את הקישור של מכשיר, קוראים לפונקציה remove() של הנהג. אם אין פונקציית remove(), הליבה עדיין יכולה לבטל את הקישור למכשיר. הליבה של מנהל ההתקן מניחה שאין צורך בפעולות ניקוי כשמנהל ההתקן מבטל את הקישור למכשיר. אם נהג לא מקושר למכשיר, אין צורך לבצע פעולת ניקוי מפורשת אם מתקיימים שני התנאים הבאים:

  • כל המשאבים שנרכשו על ידי הפונקציה probe() של מנהל התקן הם דרך ממשקי API של devm_*().

  • מכשיר החומרה לא צריך רצף כיבוי או השתקה.

במקרה כזה, הליבה של מנהל ההתקן מטפלת בשחרור כל המשאבים שנרכשו באמצעות ממשקי ה-API של devm_*(). אם אחת מההצהרות הקודמות לא נכונה, הדרייבר צריך לבצע ניקוי (שחרור משאבים וכיבוי או השבתה של החומרה) כשהקישור למכשיר מתבטל. כדי לוודא שמכשיר יכול לבטל את הקישור של מודול הנהג בצורה נקייה, צריך להשתמש באחת מהאפשרויות הבאות:

  • אם לחומרה לא נדרשת רצף כיבוי או השהיה, צריך לשנות את המודול של המכשיר כדי לקבל משאבים באמצעות ממשקי API של devm_*().

  • מטמיעים את פעולת מנהל ההתקן remove() באותו מבנה כמו הפונקציה probe(), ואז מבצעים את שלבי הניקוי באמצעות הפונקציה remove().

השבתה מפורשת של ביטול הקישור של מנהל ההתקן למכשיר (לא מומלץ)

כשבוחרים להשבית באופן מפורש את ביטול הקישור של מנהל ההתקן, צריך למנוע את ביטול הקישור וגם לא לאפשר את ביטול הטעינה של המודול.

  • כדי לא לאפשר ביטול קישור, צריך להגדיר את הדגל suppress_bind_attrs לערך true ב-struct device_driver של הנהג. ההגדרה הזו מונעת הצגה של הקבצים bind ו-unbind בספרייה sysfs של הנהג. הקובץ unbind מאפשר למשתמשים להפעיל את ביטול הקישור בין הנהג מהמכשיר שלו.

  • כדי לא לאפשר את פריקת המודול, צריך לוודא שהמודול מכיל את [permanent] ב-lsmod. אם לא משתמשים ב-module_exit() או ב-module_XXX_driver(), המודול מסומן כ-[permanent].

לא טוענים קושחת מתוך פונקציית הבדיקה

לא מומלץ לטעון את הקושחה מתוך הפונקציה .probe(), כי יכול להיות שלא תהיה גישה לקושחה אם הבדיקה של הנהג תתבצע לפני הטעינה של מערכת הקבצים שמבוססת על אחסון קבוע או זיכרון הפלאש. במקרים כאלה, יכול להיות שממשק ה-API של request_firmware*() ייחסם למשך זמן רב ואז ייכשל, מה שעלול להאט את תהליך האתחול ללא צורך. במקום זאת, כדאי לדחות את טעינת הקושחה למועד שבו הלקוח יתחיל להשתמש במכשיר. לדוגמה, מנהל התקן של המסך יכול לטעון את הקושחה כשמכשיר המסך נפתח.

שימוש ב-.probe() כדי לטעון קושחת יכול להיות בסדר במקרים מסוימים, למשל בדרייבר של שעון שצריך קושחת כדי לפעול אבל המכשיר לא חשוף למרחב המשתמש. יש תרחישי שימוש מתאימים אחרים.

הטמעת בדיקה אסינכרונית

תמיכה בבדיקה אסינכררונית ושימוש בה כדי ליהנות משיפורים עתידיים, כמו טעינת מודולים במקביל או בדיקה של המכשיר כדי לזרז את זמן האתחול, שיתווספו ל-Android בגרסאות עתידיות. מודולים של מנהלי התקנים שלא משתמשים בבדיקה אסינכררונית עלולים להפחית את היעילות של אופטימיזציות כאלה.

כדי לסמן נהג שתומך בבדיקה אסינכררונית ומעדיף אותה, מגדירים את השדה probe_type במאפיין struct device_driver של הנהג. בדוגמה הבאה מוצגת תמיכה כזו שמופעלת במנהל פלטפורמה:

static struct platform_driver acme_driver = {
        .probe          = acme_probe,
        ...
        .driver         = {
                .name   = "acme",
                ...
                .probe_type = PROBE_PREFER_ASYNCHRONOUS,
        },
};

כדי לגרום לנהג לפעול עם בדיקה אסינכרונית, לא נדרש קוד מיוחד. עם זאת, חשוב לזכור את הדברים הבאים כשמוסיפים תמיכה בבדיקה אסינכרונית:

  • אל תניחו הנחות לגבי יחסי תלות שנבדקו בעבר. בודקים באופן ישיר או עקיף (רוב הקריאות למסגרת) ומחזירים את הערך -EPROBE_DEFER אם ספק אחד או יותר עדיין לא מוכן.

  • אם מוסיפים מכשירי צאצא לפונקציית הבדיקה של מכשיר הורה, אל תניחו שהמכשירים האלה ייבדקו באופן מיידי.

  • אם הבדיקה נכשלת, מבצעים טיפול מתאים בשגיאה וניקוי (ראו שימוש בגרסאות API של devm_*()).

אין להשתמש ב-MODULE_SOFTDEP כדי להזמין בדיקות של מכשירים

הפונקציה MODULE_SOFTDEP() היא לא פתרון מהימן להבטחת הסדר של בדיקות המכשיר, אסור להשתמש בה מהסיבות הבאות.

  • בדיקה מושהית. כשמודול נטען, יכול להיות שבדיקת המכשיר תידחה כי אחד מהספקים שלו לא מוכן. זה עלול לגרום לחוסר התאמה בין סדר הטעינה של המודול לבין סדר הבדיקה של המכשיר.

  • מנהל התקן אחד, הרבה מכשירים מודול של מנהל יכול לנהל סוג מכשיר ספציפי. אם המערכת כוללת יותר ממכונה אחת של סוג מכשיר, וכל מכשיר כזה כולל דרישה שונה לסדר הבדיקה, לא תוכלו לעמוד בדרישות האלה באמצעות סדר הטעינה של המודולים.

  • בדיקה אסינכרונית מודולים של מנהלי התקנים שמבצעים בדיקה אסינכרונית לא בודקים את המכשיר באופן מיידי כשהמודול נטען. במקום זאת, בדיקת המכשיר מתבצעת על ידי שרשור מקביל, דבר שעלול להוביל לחוסר התאמה בין סדר הטעינה של המודולים לסדר בדיקת המכשיר. לדוגמה, כשמודול של מנהל התקן ראשי מסוג I2C מבצע בדיקה אסינכרונית ומודול של מנהל התקן מגע תלוי ב-PMIC שנמצא באוטובוס I2C, גם אם מנהל ההתקן של המגע ומנהל ההתקן של ה-PMIC נטענים בסדר הנכון, יכול להיות שיתבצע ניסיון לבדיקת מנהל ההתקן של המגע לפני הבדיקה של מנהל ההתקן של ה-PMIC.

אם יש לכם מודולים של מנהלי התקנים שמשתמשים בפונקציה MODULE_SOFTDEP(), צריך לתקן אותם כך שלא ישתמשו בפונקציה הזו. כדי לעזור לך, צוות Android העביר לשינויים ל-upstream, שמאפשרים לליבה לטפל בבעיות שקשורות לסדר בלי להשתמש ב-MODULE_SOFTDEP(). באופן ספציפי, אפשר להשתמש ב-fw_devlink כדי לוודא את סדר הבדיקה, ולאחר שכל הצרכנים של המכשיר יבצעו בדיקה, להשתמש בקריאה החוזרת (callback) sync_state() כדי לבצע את המשימות הנדרשות.

שימוש ב-#if IS_ENABLED()‎ במקום ב-#ifdef להגדרות

משתמשים ב-#if IS_ENABLED(CONFIG_XXX) במקום ב-#ifdef CONFIG_XXX כדי לוודא שהקוד בתוך הבלוק #if ימשיך להדר אם ההגדרה תשתנה בעתיד להגדרה של tristate. ההבדלים הם:

  • הערך של #if IS_ENABLED(CONFIG_XXX) מחושב כ-true כאשר הערך של CONFIG_XXX מוגדר כמודול (=m) או כתוכנה מובנית (=y).

  • הערך של #ifdef CONFIG_XXX מחושב כ-true כשהערך של CONFIG_XXX מוגדר כ'מוטמע' (=y) , אבל לא כשהערך של CONFIG_XXX מוגדר כ'מודול' (=m). צריך להשתמש באפשרות הזו רק אם אתם בטוחים שאתם רוצים לעשות את אותו הדבר כשהתצורה מוגדרת כ'מודול' או כ'מושבתת'.

שימוש במאקרו הנכון ל-compiles מותנים

אם CONFIG_XXX מוגדר למודול (=m), מערכת ה-build מגדירה את CONFIG_XXX_MODULE באופן אוטומטי. אם הנהג נשלט על ידי CONFIG_XXX ואתם רוצים לבדוק אם הוא מופעל כמודול, תוכלו להיעזר בהנחיות הבאות:

  • בקובץ C (או בכל קובץ מקור שאינו קובץ כותרת) של מנהל ההתקן, אל תשתמשו ב-#ifdef CONFIG_XXX_MODULE כי הוא מגביל שלא לצורך, ונשבר אם שם ההגדרה השתנה ל-CONFIG_XYZ. לכל קובץ מקור ללא כותרת שמשולב במודול, מערכת ה-build מגדירה באופן אוטומטי את MODULE להיקף הקובץ. לכן, כדי לבדוק אם קובץ C (או כל קובץ מקור שאינו כותרת) עובר הידור כחלק ממודול, משתמשים ב-#ifdef MODULE (ללא הקידומת CONFIG_).

  • בקבצי הכותרת, אותה בדיקה מורכבת יותר כי קבצי הכותרת לא עוברים הידור ישירות לקובץ בינארי, אלא כחלק מקובץ C (או מקובצי מקור אחרים). יש להשתמש בכללים הבאים לגבי קובצי הכותרת:

    • בקובץ כותרת שמשתמש ב-#ifdef MODULE, התוצאה משתנה בהתאם לקובץ המקור שבו נעשה בו שימוש. כלומר, באותו קובץ כותרת באותו build יכולים להיות חלקים שונים של הקוד שעבר הידור עבור קובצי מקור שונים (מודול לעומת מובנה או מושבת). האפשרות הזו יכולה להיות שימושית כשרוצים להגדיר מאקרו שצריך להתרחב בדרך אחת לקוד מובנה ובדרך אחרת למודול.

    • בקובץ כותרת שצריך להדרג קטע קוד כשCONFIG_XXX ספציפי מוגדר כמודול (ללא קשר אם קובץ המקור שכולל אותו הוא מודול), צריך להשתמש ב-#ifdef CONFIG_XXX_MODULE בקובץ הכותרת.