إرشادات وحدة البائع

استخدم الإرشادات التالية لزيادة قوة وموثوقية وحدات البائع الخاصة بك. يمكن أن تساعد العديد من الإرشادات، عند اتباعها، في تسهيل تحديد ترتيب تحميل الوحدة الصحيح والترتيب الذي يجب على برامج التشغيل التحقق من الأجهزة به.

يمكن أن تكون الوحدة مكتبة أو برنامج تشغيل .

  • وحدات المكتبة هي مكتبات توفر واجهات برمجة التطبيقات (APIs) لاستخدام الوحدات الأخرى. عادةً لا تكون هذه الوحدات خاصة بالأجهزة. تتضمن أمثلة وحدات المكتبة وحدة تشفير AES، وإطار عمل remoteproc الذي تم تجميعه كوحدة نمطية، ووحدة مخزن السجل. يتم تشغيل رمز الوحدة في module_init() لإعداد بنيات البيانات، ولكن لا يتم تشغيل أي تعليمات برمجية أخرى ما لم يتم تشغيلها بواسطة وحدة نمطية خارجية.

  • وحدات برنامج التشغيل هي برامج تشغيل تقوم بالتحقق من نوع معين من الأجهزة أو الارتباط به. هذه الوحدات خاصة بالأجهزة. تتضمن أمثلة وحدات التشغيل UART وPCIe وأجهزة تشفير الفيديو. يتم تنشيط وحدات برنامج التشغيل فقط عندما يكون الجهاز المرتبط بها موجودًا على النظام.

    • إذا لم يكن الجهاز موجودًا، فإن رمز الوحدة الوحيد الذي يتم تشغيله هو رمز module_init() الذي يسجل برنامج التشغيل مع إطار عمل برنامج التشغيل الأساسي.

    • إذا كان الجهاز موجودًا ونجح برنامج التشغيل في التحقق من هذا الجهاز أو الارتباط به، فقد يتم تشغيل رمز وحدة نمطية آخر.

استخدم الوحدة النمطية init/exit بشكل صحيح

يجب على وحدات برنامج التشغيل تسجيل برنامج تشغيل في 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 أو بيانات aux الخاصة بالجهاز بدلاً من تسجيل برامج تشغيل منفصلة. وبدلاً من ذلك، يمكنك تقسيم وحدة برنامج التشغيل إلى وحدتين.

استثناءات وظيفة التهيئة والخروج

لا تسجل وحدات المكتبة برامج التشغيل وتُعفى من القيود المفروضة على module_init() و module_exit() لأنها قد تحتاج إلى هذه الوظائف لإعداد بنيات البيانات أو قوائم انتظار العمل أو سلاسل عمليات kernel.

استخدم الماكرو MODULE_DEVICE_TABLE

يجب أن تتضمن وحدات برنامج التشغيل الماكرو MODULE_DEVICE_TABLE ، الذي يسمح لمساحة المستخدم بتحديد الأجهزة التي تدعمها وحدة برنامج التشغيل قبل تحميل الوحدة. يمكن لنظام Android استخدام هذه البيانات لتحسين تحميل الوحدات، مثل تجنب تحميل الوحدات للأجهزة غير الموجودة في النظام. للحصول على أمثلة حول استخدام الماكرو، راجع التعليمات البرمجية الأولية.

تجنب حالات عدم تطابق CRC بسبب أنواع البيانات المعلنة

لا تقم بتضمين ملفات الرأس للحصول على رؤية لأنواع البيانات المعلنة. يمكن إعادة توجيه بعض البنيات والاتحادات وأنواع البيانات الأخرى المحددة في ملف رأس ( header-Ah ) في ملف رأس مختلف ( header-Bh ) يستخدم عادةً مؤشرات لأنواع البيانات هذه. يعني نمط التعليمات البرمجية هذا أن النواة تحاول عمدًا الحفاظ على خصوصية بنية البيانات لمستخدمي header-Bh .

يجب ألا يقوم مستخدمو header-Bh بتضمين header-Ah للوصول مباشرة إلى الأجزاء الداخلية لهياكل البيانات المعلنة هذه. يؤدي القيام بذلك إلى حدوث مشكلات عدم تطابق CONFIG_MODVERSIONS CRC (مما يؤدي إلى حدوث مشكلات في توافق 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 الأساسية أو تعديلها إلى سلوك غير مرغوب فيه، بما في ذلك تسرب الذاكرة والتعطل وعدم التوافق مع إصدارات kernel المستقبلية. تعتبر بنية البيانات بنية بيانات kernel أساسية عندما تستوفي أيًا من الشروط التالية:

  • يتم تعريف بنية البيانات ضمن 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() .

يمكنك الوصول إلى هياكل بيانات kernel الأساسية فقط من خلال الوظائف المصدرة بواسطة kernel أو من خلال المعلمات التي تم تمريرها بشكل صريح كمدخلات إلى خطافات البائع. إذا لم يكن لديك واجهة برمجة تطبيقات أو ربط بائع لتعديل أجزاء من بنية بيانات kernel الأساسية، فمن المحتمل أن يكون ذلك مقصودًا ويجب ألا تقوم بتعديل بنية البيانات من الوحدات النمطية. على سبيل المثال، لا تقم بتعديل أي حقول داخل struct device أو struct device.links .

  • لتعديل device.devres_head ، استخدم دالة devm_*() مثل devm_clk_get() أو devm_regulator_get() أو devm_kzalloc() .

  • لتعديل الحقول داخل struct device.links ، استخدم واجهة برمجة تطبيقات رابط الجهاز مثل device_link_add() أو device_link_del() .

لا تقم بتحليل عقد Devicetree ذات الخصائص المتوافقة

إذا كانت عقدة شجرة الأجهزة (DT) تحتوي على خاصية compatible ، فسيتم تخصيص struct device لها تلقائيًا أو عندما يتم استدعاء of_platform_populate() على عقدة DT الأصلية (عادةً بواسطة برنامج تشغيل الجهاز الخاص بالجهاز الأصلي). التوقع الافتراضي (باستثناء بعض الأجهزة التي تمت تهيئتها مبكرًا للمجدول) هو أن عقدة 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 للبحث عن الموردين

قم بالرجوع إلى المورد باستخدام المقبض (مرجع/مؤشر إلى عقدة DT) في DT كلما أمكن ذلك. يؤدي استخدام روابط DT القياسية ومقابضها للإشارة إلى الموردين إلى تمكين 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"
      };
    };
    

لا تقم بتعديل أخطاء واجهة برمجة تطبيقات الإطار

تقوم واجهات برمجة التطبيقات الخاصة بإطار العمل، مثل regulator clocks و irq و gpio و phys و extcon ، بإرجاع -EPROBE_DEFER كقيمة إرجاع خطأ للإشارة إلى أن الجهاز يحاول التحقيق ولكن لا يمكنه ذلك في الوقت الحالي، ويجب على kernel إعادة محاولة التحقيق لاحقاً. للتأكد من فشل وظيفة .probe() بجهازك كما هو متوقع في مثل هذه الحالات، لا تستبدل قيمة الخطأ أو تعيد تعيينها. قد يؤدي استبدال قيمة الخطأ أو إعادة تعيينها إلى إسقاط -EPROBE_DEFER ويؤدي إلى عدم فحص جهازك مطلقًا.

استخدم متغيرات واجهة برمجة التطبيقات devm_*()

عندما يحصل الجهاز على مورد باستخدام devm_*() API، يتم تحرير المورد تلقائيًا بواسطة kernel إذا فشل الجهاز في التحقيق، أو التحقيق بنجاح وتم فك ربطه لاحقًا. تعمل هذه الوظيفة على تنظيف كود معالجة الأخطاء في وظيفة probe() لأنها لا تتطلب goto الانتقال لتحرير الموارد التي تم الحصول عليها بواسطة devm_*() وتبسيط عمليات إلغاء ربط برنامج التشغيل.

التعامل مع فك ربط برنامج تشغيل الجهاز

كن متعمدًا بشأن إلغاء ربط برامج تشغيل الأجهزة ولا تترك إلغاء الربط غير محدد لأن غير المحدد لا يعني ضمنيًا أنه غير مسموح به. يجب عليك إما تنفيذ إلغاء ربط برنامج تشغيل الجهاز بالكامل أو تعطيل إلغاء ربط برنامج تشغيل الجهاز بشكل صريح.

تنفيذ إلغاء ربط برنامج تشغيل الجهاز

عند اختيار التنفيذ الكامل لإلغاء ربط برنامج تشغيل الجهاز، قم بإلغاء ربط برامج تشغيل الجهاز بشكل نظيف لتجنب تسرب الذاكرة أو الموارد ومشكلات الأمان. يمكنك ربط جهاز ببرنامج التشغيل عن طريق استدعاء وظيفة probe() الخاصة بالسائق وإلغاء ربط الجهاز عن طريق استدعاء وظيفة remove() ببرنامج التشغيل. إذا لم تكن هناك وظيفة remove() ، فلا يزال بإمكان النواة فك ربط الجهاز؛ يفترض جوهر برنامج التشغيل أن برنامج التشغيل لا يحتاج إلى أي عمل تنظيف عند فك ربطه من الجهاز. لا يحتاج برنامج التشغيل غير المرتبط بالجهاز إلى القيام بأي عمل تنظيف واضح عندما ينطبق الأمران التاليان:

  • جميع الموارد التي يتم الحصول عليها بواسطة وظيفة probe() الخاصة بالسائق تكون من خلال واجهات برمجة تطبيقات devm_*() .

  • لا يحتاج الجهاز إلى إيقاف التشغيل أو تسلسل السكون.

في هذه الحالة، يعالج جوهر برنامج التشغيل تحرير جميع الموارد المكتسبة من خلال واجهات برمجة تطبيقات devm_*() . إذا كانت أي من العبارات السابقة غير صحيحة، فسيحتاج برنامج التشغيل إلى إجراء عملية التنظيف (تحرير الموارد وإيقاف تشغيل الجهاز أو إخماده) عند إلغاء ربطه بالجهاز. للتأكد من أن الجهاز يمكنه إلغاء ربط وحدة برنامج التشغيل بشكل نظيف، استخدم أحد الخيارات التالية:

  • إذا لم يكن الجهاز بحاجة إلى إيقاف التشغيل أو تسلسل السكون، فقم بتغيير وحدة الجهاز للحصول على الموارد باستخدام devm_*() APIs.

  • قم بتنفيذ عملية remove() لبرنامج التشغيل في نفس بنية وظيفة probe() ، ثم قم بخطوات التنظيف باستخدام وظيفة remove() .

تعطيل إلغاء ربط برنامج تشغيل الجهاز بشكل صريح (غير مستحسن)

عند اختيار تعطيل إلغاء ربط برنامج تشغيل الجهاز بشكل صريح، فإنك تحتاج إلى عدم السماح بإلغاء الربط وعدم السماح بتفريغ الوحدة النمطية.

  • لعدم السماح بإلغاء الربط، قم بتعيين علامةقمع_ suppress_bind_attrs على true في struct device_driver ؛ يمنع هذا الإعداد ظهور ملفات bind unbind في دليل sysfs الخاص ببرنامج التشغيل. ملف unbind هو ما يسمح لمساحة المستخدم بتشغيل إلغاء ربط برنامج التشغيل من جهازه.

  • لعدم السماح بتفريغ الوحدة، تأكد من أن الوحدة تحتوي على [permanent] في lsmod . من خلال عدم استخدام module_exit() أو module_XXX_driver() ، يتم وضع علامة على الوحدة على أنها [permanent] .

لا تقم بتحميل البرامج الثابتة من داخل وظيفة المسبار

لا ينبغي لبرنامج التشغيل تحميل البرنامج الثابت من داخل وظيفة .probe() لأنه قد لا يتمكن من الوصول إلى البرنامج الثابت إذا قام برنامج التشغيل بالتحقق قبل تثبيت نظام الملفات المعتمد على الفلاش أو التخزين الدائم. في مثل هذه الحالات، قد يتم حظر واجهة برمجة تطبيقات 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 إذا كان واحد أو أكثر من الموردين غير جاهزين بعد.

  • إذا قمت بإضافة أجهزة فرعية في وظيفة فحص الجهاز الأصلي، فلا تفترض أنه تم فحص الأجهزة الفرعية على الفور.

  • في حالة فشل المسبار، قم بإجراء معالجة مناسبة للأخطاء وتنظيفها (راجع استخدام متغيرات واجهة برمجة التطبيقات devm_*() ).

لا تستخدم MODULE_SOFTDEP لطلب مجسات الجهاز

لا تعد وظيفة MODULE_SOFTDEP() حلاً موثوقًا به لضمان ترتيب مجسات الجهاز ويجب عدم استخدامها للأسباب التالية.

  • التحقيق المؤجل. عند تحميل وحدة نمطية، قد يتم تأجيل فحص الجهاز لأن أحد مورديها ليس جاهزًا. يمكن أن يؤدي هذا إلى عدم تطابق بين ترتيب تحميل الوحدة وترتيب مسبار الجهاز.

  • سائق واحد، والعديد من الأجهزة. يمكن لوحدة التشغيل إدارة نوع جهاز معين. إذا كان النظام يتضمن أكثر من مثيل واحد لنوع الجهاز وكان لكل من هذه الأجهزة متطلبات مختلفة لأمر التحقيق، فلا يمكنك احترام هذه المتطلبات باستخدام ترتيب تحميل الوحدة النمطية.

  • التحقيق غير المتزامن. لا تقوم وحدات برنامج التشغيل التي تقوم بإجراء فحص غير متزامن باختبار الجهاز على الفور عند تحميل الوحدة. بدلاً من ذلك، يتعامل الخيط المتوازي مع فحص الجهاز، مما قد يؤدي إلى عدم التطابق بين ترتيب تحميل الوحدة وترتيب مسبار الجهاز. على سبيل المثال، عندما تقوم وحدة برنامج التشغيل الرئيسية I2C بإجراء اختبار غير متزامن وتعتمد وحدة برنامج تشغيل اللمس على PMIC الموجود على ناقل I2C، حتى إذا تم تحميل برنامج تشغيل اللمس وبرنامج تشغيل PMIC بالترتيب الصحيح، فقد تتم محاولة إجراء اختبار برنامج تشغيل اللمس قبل مسبار السائق PMIC.

إذا كان لديك وحدات برامج تشغيل تستخدم الدالة MODULE_SOFTDEP() ، فقم بإصلاحها بحيث لا تستخدم هذه الوظيفة. لمساعدتك، قام فريق Android بنقل التغييرات التي تمكن النواة من معالجة مشكلات الطلب دون استخدام MODULE_SOFTDEP() . على وجه التحديد، يمكنك استخدام fw_devlink لضمان طلب التحقيق و(بعد قيام جميع مستهلكي الجهاز بالتحقق) استخدم رد الاتصال sync_state() لتنفيذ أي مهام ضرورية.

استخدم #if IS_ENABLED() بدلاً من #ifdef للتكوينات

استخدم #if IS_ENABLED(CONFIG_XXX) بدلاً من #ifdef CONFIG_XXX للتأكد من أن الكود الموجود داخل كتلة #if يستمر في التجميع إذا تغير التكوين إلى تكوين ثلاثي في ​​المستقبل. الاختلافات هي كما يلي:

  • #if IS_ENABLED(CONFIG_XXX) على أنه true عند تعيين CONFIG_XXX على الوحدة النمطية ( =m ) أو المضمنة ( =y ).

  • يتم تقييم #ifdef CONFIG_XXX على أنه true عندما يتم تعيين CONFIG_XXX على ( =y ) المضمن، ولكن لا يتم تقييمه على أنه صحيح عندما يتم تعيين CONFIG_XXX على الوحدة ( =m ). استخدم هذا فقط عندما تكون متأكدًا من رغبتك في القيام بنفس الشيء عند تعيين التكوين على الوحدة النمطية أو تعطيله.

استخدم الماكرو الصحيح للتجميعات الشرطية

إذا تم تعيين CONFIG_XXX على الوحدة النمطية ( =m ) ، فسيقوم نظام الإنشاء تلقائيًا بتعريف CONFIG_XXX_MODULE . إذا كان برنامج التشغيل الخاص بك يتم التحكم فيه بواسطة CONFIG_XXX وتريد التحقق مما إذا كان برنامج التشغيل الخاص بك يتم تجميعه كوحدة نمطية، فاستخدم الإرشادات التالية:

  • في ملف C (أو أي ملف مصدر ليس ملف رأس) لبرنامج التشغيل الخاص بك، لا تستخدم #ifdef CONFIG_XXX_MODULE لأنه مقيد بشكل غير ضروري وينقطع إذا تمت إعادة تسمية التكوين إلى CONFIG_XYZ . بالنسبة لأي ملف مصدر غير رأسي يتم تجميعه في وحدة نمطية، يقوم نظام الإنشاء تلقائيًا بتعريف MODULE لنطاق هذا الملف. لذلك، للتحقق مما إذا كان يتم تجميع ملف C (أو أي ملف مصدر غير رأسي) كجزء من وحدة نمطية، استخدم #ifdef MODULE (بدون البادئة CONFIG_ ).

  • في ملفات الرأس، يكون الفحص نفسه أكثر صعوبة لأن ملفات الرأس لا يتم تجميعها مباشرة في ملف ثنائي، بل يتم تجميعها كجزء من ملف C (أو ملفات مصدر أخرى). استخدم القواعد التالية لملفات الرأس:

    • بالنسبة لملف الرأس الذي يستخدم #ifdef MODULE ، تتغير النتيجة بناءً على الملف المصدر الذي يستخدمه. هذا يعني أن نفس ملف الرأس في نفس البنية يمكن أن يحتوي على أجزاء مختلفة من التعليمات البرمجية الخاصة به مجمعة لملفات مصدر مختلفة (الوحدة النمطية مقابل المضمنة أو المعطلة). يمكن أن يكون هذا مفيدًا عندما تريد تحديد ماكرو يحتاج إلى التوسيع بطريقة واحدة للتعليمات البرمجية المضمنة والتوسيع بطريقة مختلفة للوحدة النمطية.

    • بالنسبة لملف الرأس الذي يحتاج إلى تجميع جزء من التعليمات البرمجية عند تعيين CONFIG_XXX محددًا على الوحدة النمطية (بغض النظر عما إذا كان الملف المصدر بما في ذلك وحدة نمطية)، يجب أن يستخدم ملف الرأس #ifdef CONFIG_XXX_MODULE .