הנחיות בנושא מודולים של ספקים

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

מודול יכול להיות ספרייה או דרייבר.

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

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

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

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

שימוש נכון בפונקציות init ויציאה של מודולים

מודולים של מנהלי התקנים חייבים לרשום מנהל התקן ב-module_init() ולבטל את הרישום של מנהל התקן ב-module_exit(). אחת הדרכים לאכוף את ההגבלות האלה היא באמצעות מאקרו עוטף, שמונע שימוש ישיר במאקרו 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 או בנתוני העזר של המכשיר במקום לרשום מנהלי התקנים נפרדים. אפשרות אחרת היא לפצל את מודול הדרייבר לשני מודולים.

חריגים בפונקציות 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 כדי לגשת ישירות לחלקים הפנימיים של מבני הנתונים האלה שהוכרזו מראש. כתוצאה מכך, כשליבה אחרת (כמו ליבת GKI) מנסה לטעון את המודול, נוצרות בעיות של אי התאמה ב-CONFIG_MODVERSIONSCRC (שיוצרות בעיות תאימות ל-ABI).

לדוגמה, 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 לא נכללים בבדיקה.

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

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

הגישה למבני נתונים של ליבת הקרנל מתבצעת רק באמצעות פונקציות שמיוצאות על ידי הקרנל או באמצעות פרמטרים שמועברים באופן מפורש כקלט ל-vendor hooks. אם אין לכם API או ווֹקְס (hooks) של ספקים כדי לשנות חלקים ממבנה נתונים של ליבת קרנל, כנראה שזה מכוון ואסור לכם לשנות את מבנה הנתונים ממודולים. לדוגמה, אל תשנו שדות בתוך 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 עם מאפיין compatible כולל struct device ומנהל התקן תואם. כל החריגים האחרים כבר מטופלים בקוד במעלה הזרם.

בנוסף, 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 (אפשרות שקיימת רק אם הוא לא הועבר למאגר המרכזי). כדי לדון בחלופות, אפשר לפנות לצוות ליבת Android בכתובת kernel-team@android.com ולהיות מוכנים להצדיק את תרחישי השימוש שלכם.

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

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

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

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

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

אחרי שהוספנו תמיכה ב-DT לליבות ARM, הצרכנים יכולים לזהות ספקים ב-DT באמצעות הפניה לצומת של עץ המכשיר של הספק באמצעות phandle. צרכנים יכולים גם לתת שם למשאב על סמך השימוש בו, במקום על סמך מי שמספק אותו. לדוגמה, מנהל ההתקן של המסך הדיגיטלי מהדוגמה הקודמת יכול להשתמש ב-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 כשגיאה כדי לציין שמכשיר מנסה לבצע בדיקה אבל לא יכול לעשות זאת כרגע, ושהליבה צריכה לנסות שוב לבצע את הבדיקה מאוחר יותר. כדי לוודא שהפונקציה .probe() של המכשיר תיכשל כצפוי במקרים כאלה, אל תחליפו את ערך השגיאה או תשנו את המיפוי שלו. החלפה או מיפוי מחדש של ערך השגיאה עלולים לגרום להשמטה של -EPROBE_DEFER ולמצב שבו המכשיר לא ייבדק אף פעם.

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

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

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

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

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

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

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

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

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

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

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

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

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

  • כדי לא לאפשר ביטול של הקישור, צריך להגדיר את הדגל 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 העביר שינויים במעלה הזרם שמאפשרים לליבה לטפל בבעיות של סדר בלי להשתמש ב-MODULE_SOFTDEP(). באופן ספציפי, אפשר להשתמש ב-fw_devlink כדי לוודא שהסדר של הבדיקה יהיה נכון, ואחרי שכל הצרכנים של המכשיר יבצעו בדיקה, אפשר להשתמש בקריאה החוזרת sync_state() כדי לבצע את המשימות הנדרשות.

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

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

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

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

שימוש במאקרו הנכון לקומפילציות מותנות

אם 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, התוצאה משתנה בהתאם לקובץ המקור שמשתמש בו. כלומר, יכול להיות שחלקים שונים בקובץ הכותרת הזה יקומפלו עבור קובצי מקור שונים (מודול לעומת מובנה או מושבת). האפשרות הזו יכולה להיות שימושית כשרוצים להגדיר פקודת מאקרו שצריכה להתרחב בדרך מסוימת עבור קוד מובנה, ובדרך אחרת עבור מודול.

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