ההנחיות הבאות יעזרו לכם לשפר את החוסן והאמינות של מודולי הספקים. יש הרבה הנחיות שאם פועלים לפיהן, קל יותר לקבוע את סדר הטעינה הנכון של המודולים ואת הסדר שבו מנהלי ההתקנים צריכים לבדוק את המכשירים.
מודול יכול להיות ספרייה או דרייבר.
מודולים של ספריות הם ספריות שמספקות ממשקי 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_MODVERSIONS
CRC (שיוצרות בעיות תאימות ל-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 או ווֹנדור (vendor) כדי לשנות חלקים ממבנה נתונים של ליבת קרנל, כנראה שזה מכוון ואסור לכם לשנות את מבנה הנתונים ממודולים. לדוגמה, אל תשנו שדות בתוך 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()
.
לא לנתח צמתים של עץ המכשירים עם מאפיין תואם
אם לצומת של עץ המכשירים (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 בכתובת 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()
באותה מבנה כמו הפונקציהprobe()
, ואז מבצעים את שלבי הניקוי באמצעות הפונקציהremove()
.
השבתה מפורשת של ביטול הקישור של מנהל ההתקן (לא מומלץ)
כשבוחרים להשבית באופן מפורש את ביטול הקישור של מנהל התקן, צריך לאסור את ביטול הקישור וגם לאסור את ביטול הטעינה של המודול.
כדי למנוע ביטול של הקישור, צריך להגדיר את הדגל
suppress_bind_attrs
לערךtrue
ב-struct device_driver
של מנהל ההתקן. ההגדרה הזו מונעת את הצגת הקבציםbind
ו-unbind
בספרייהsysfs
של מנהל ההתקן. הקובץunbind
הוא מה שמאפשר למרחב המשתמשים להפעיל את ביטול הקישור של מנהל התקן מהמכשיר שלו.כדי לא לאפשר את הסרת הטעינה של המודול, מוודאים שהמודול כולל את
[permanent]
ב-lsmod
. אם לא משתמשים ב-module_exit()
או ב-module_XXX_driver()
, המודול מסומן כ-[permanent]
.
לא לטעון קושחה מתוך פונקציית הבדיקה
הדרייבר לא אמור לטעון קושחה מתוך הפונקציה .probe()
, כי יכול להיות שלא תהיה לו גישה לקושחה אם הוא יבדוק לפני שהמערכת תטען את ה-flash או את מערכת הקבצים שמבוססת על אחסון קבוע. במקרים כאלה, יכול להיות ש-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
.