دستورالعمل های ماژول فروشنده

از دستورالعمل های زیر برای افزایش استحکام و قابلیت اطمینان ماژول های فروشنده خود استفاده کنید. بسیاری از دستورالعمل‌ها، در صورت رعایت کردن، می‌توانند به آسان‌تر شدن تعیین ترتیب بارگذاری صحیح ماژول و ترتیبی که درایورها باید دستگاه‌ها را بررسی کنند، کمک کنند.

یک ماژول می تواند یک کتابخانه یا یک درایور باشد.

  • ماژول‌های کتابخانه ، کتابخانه‌هایی هستند که APIهایی را برای استفاده از ماژول‌های دیگر فراهم می‌کنند. چنین ماژول هایی معمولاً سخت افزاری خاص نیستند. نمونه‌هایی از ماژول‌های کتابخانه عبارتند از یک ماژول رمزگذاری AES، چارچوب remoteproc که به عنوان یک ماژول کامپایل شده است، و یک ماژول logbuffer. کد ماژول در 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 دستگاه به‌جای ثبت درایورهای جداگانه، آن را متمایز کنید. از طرف دیگر، می توانید ماژول درایور را به دو ماژول تقسیم کنید.

استثناهای تابع شروع و خروج

ماژول‌های کتابخانه درایورها را ثبت نمی‌کنند و از محدودیت‌های module_init() و module_exit() معاف هستند، زیرا ممکن است برای راه‌اندازی ساختارهای داده، صف‌های کاری یا رشته‌های هسته به این توابع نیاز داشته باشند.

از ماکرو 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-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() برگردانده شده است.

دسترسی به ساختارهای داده هسته هسته فقط از طریق توابع صادر شده توسط هسته یا از طریق پارامترهایی که به صراحت به عنوان ورودی به قلاب های فروشنده ارسال می شوند. اگر یک 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() استفاده کنید.

گره‌های درخت دستگاه را با ویژگی سازگار تجزیه نکنید

اگر یک گره درخت دستگاه (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 استفاده کنید

در صورت امکان به یک تامین کننده با استفاده از یک phandle (یک مرجع/نشانگر به گره DT) در 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، مصرف کنندگان می توانند با مراجعه به گره درختی دستگاه تامین کننده با استفاده از یک phandle، تامین کنندگان را در 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 را به‌عنوان یک مقدار خطا برمی‌گرداند تا نشان دهد دستگاهی در حال تلاش برای بررسی است اما در این زمان نمی‌تواند، و هسته باید دوباره تلاش کند کاوشگر را انجام دهد. بعد. برای اطمینان از اینکه عملکرد .probe() دستگاه شما همانطور که انتظار می رود در چنین مواردی با شکست مواجه می شود، مقدار خطا را جایگزین یا دوباره ترسیم نکنید. جایگزین کردن یا ترسیم مجدد مقدار خطا ممکن است باعث شود -EPROBE_DEFER حذف شود و باعث شود دستگاه شما هرگز کاوش نشود.

از انواع API devm_*() استفاده کنید

هنگامی که دستگاه با استفاده از یک API devm_*() منبعی را به دست می‌آورد، اگر دستگاه نتواند کاوش کند، منبع به‌طور خودکار توسط هسته آزاد می‌شود، یا با موفقیت بررسی می‌شود و بعداً جدا می‌شود. این قابلیت باعث می‌شود کد مدیریت خطا در تابع probe() پاک‌تر شود، زیرا برای آزاد کردن منابع بدست‌آمده توسط devm_*() به goto jump نیاز ندارد و عملیات unbinding درایور را ساده می‌کند.

کنترل باز کردن اتصال دستگاه-درایور

در مورد بازکردن درایورهای دستگاه عمدی باشید و unbinding را تعریف نشده رها نکنید زیرا undefined به معنای غیر مجاز نیست. شما باید یا به طور کامل unbinding دستگاه-درایور را اجرا کنید یا صریحاً unbinding دستگاه-درایور را غیرفعال کنید.

اجرای unbinding دستگاه-درایور

هنگام انتخاب اجرای کامل unbinding دستگاه-درایور، درایورهای دستگاه را به طور تمیز باز کنید تا از نشت حافظه یا منابع و مشکلات امنیتی جلوگیری کنید. می‌توانید با فراخوانی probe() درایور، یک دستگاه را به درایور متصل کنید و با فراخوانی تابع remove() درایور، یک دستگاه را جدا کنید. اگر تابع remove() وجود نداشته باشد، هسته همچنان می تواند دستگاه را جدا کند. هسته درایور فرض می کند که هنگام جدا شدن از دستگاه، هیچ کار پاکسازی توسط درایور لازم نیست. درایوری که از دستگاه جدا شده است، نیازی به انجام هیچ کار پاکسازی واضحی ندارد، در صورتی که هر دو مورد زیر درست باشد:

  • تمام منابع بدست آمده توسط تابع probe() راننده از طریق APIهای devm_*() می باشد.

  • دستگاه سخت افزاری نیازی به خاموش شدن یا توالی خاموشی ندارد.

در این شرایط، هسته درایور آزاد کردن تمام منابع به دست آمده از طریق APIهای devm_*() را کنترل می کند. اگر هر یک از عبارات قبلی نادرست است، درایور باید پاکسازی را انجام دهد (منابع را آزاد کند و سخت‌افزار را خاموش یا خاموش کند) هنگامی که از دستگاه جدا می‌شود. برای اطمینان از اینکه دستگاه می تواند ماژول درایور را به طور تمیز باز کند، از یکی از گزینه های زیر استفاده کنید:

  • اگر سخت افزار به دنباله خاموش کردن یا خاموش کردن نیاز ندارد، ماژول دستگاه را تغییر دهید تا منابع را با استفاده از APIهای devm_*() بدست آورید.

  • عملیات درایور remove() را در همان ساختار تابع probe() پیاده سازی کنید، سپس مراحل پاکسازی را با استفاده از تابع remove() انجام دهید.

غیرفعال کردن صریح حذف اتصال درایور دستگاه (توصیه نمی شود)

هنگام انتخاب غیرفعال کردن صریح unbinding دستگاه-درایور، باید unbinding و غیرفعال کردن ماژول را غیرفعال کنید.

  • برای غیر مجاز کردن unbinding، پرچم 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 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 برای اطمینان از سفارش پروب استفاده کنید و (بعد از اینکه همه مصرف‌کنندگان یک دستگاه بررسی کردند) از callback sync_state() برای انجام کارهای ضروری استفاده کنید.

از #if IS_ENABLED() به جای #ifdef برای تنظیمات استفاده کنید

از #if IS_ENABLED(CONFIG_XXX) به جای #ifdef CONFIG_XXX استفاده کنید تا مطمئن شوید که در صورت تغییر پیکربندی به پیکربندی سه‌گانه در آینده، کد داخل بلوک #if به کامپایل ادامه می‌دهد. تفاوت ها به شرح زیر است:

  • #if IS_ENABLED(CONFIG_XXX) زمانی که CONFIG_XXX روی ماژول ( =m ) یا داخلی ( =y ) تنظیم شده باشد، به true ارزیابی می‌شود.

  • #ifdef CONFIG_XXX زمانی که CONFIG_XXX روی داخلی ( =y ) تنظیم شده باشد، true ارزیابی می‌کند، اما وقتی 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 استفاده کند.