वेंडर मॉड्यूल के लिए दिशा-निर्देश

वेंडर मॉड्यूल की मज़बूती और विश्वसनीयता बढ़ाने के लिए, यहां दिए गए दिशा-निर्देशों का पालन करें. कई दिशा-निर्देशों का पालन करने से, मॉड्यूल लोड करने का सही क्रम और डिवाइसों की जांच करने के लिए ड्राइवर का क्रम तय करना आसान हो जाता है.

कोई मॉड्यूल, लाइब्रेरी या ड्राइवर हो सकता है.

  • लाइब्रेरी मॉड्यूल, ऐसी लाइब्रेरी होती हैं जो अन्य मॉड्यूल के इस्तेमाल के लिए एपीआई उपलब्ध कराती हैं. आम तौर पर, इस तरह के मॉड्यूल हार्डवेयर के हिसाब से तय नहीं होते. लाइब्रेरी मॉड्यूल के उदाहरणों में, एईएस एन्क्रिप्शन मॉड्यूल, remoteproc फ़्रेमवर्क (जिसे मॉड्यूल के तौर पर कंपाइल किया जाता है) और लॉगबफ़र मॉड्यूल शामिल हैं. module_init() में मौजूद मॉड्यूल कोड, डेटा स्ट्रक्चर सेट अप करने के लिए चलता है. हालांकि, कोई अन्य कोड तब तक नहीं चलता, जब तक उसे किसी बाहरी मॉड्यूल से ट्रिगर न किया जाए.

  • ड्राइवर मॉड्यूल, ऐसे ड्राइवर होते हैं जो किसी खास तरह के डिवाइस की जांच करते हैं या उससे बाइंड होते हैं. इस तरह के मॉड्यूल हार्डवेयर के हिसाब से तय होते हैं. ड्राइवर मॉड्यूल के उदाहरणों में, यूएआरटी, पीसीआईई, और वीडियो एनकोडर हार्डवेयर शामिल हैं. ड्राइवर मॉड्यूल सिर्फ़ तब चालू होते हैं, जब उनसे जुड़ा डिवाइस सिस्टम पर मौजूद हो.

    • अगर डिवाइस मौजूद नहीं है, तो सिर्फ़ 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().

builtin_subsystem_driver()

कुछ ड्राइवर मॉड्यूल, module_init() और module_exit() का इस्तेमाल करते हैं, क्योंकि वे एक से ज़्यादा ड्राइवर रजिस्टर करते हैं. एक से ज़्यादा ड्राइवर रजिस्टर करने के लिए, module_init() और module_exit() का इस्तेमाल करने वाले ड्राइवर मॉड्यूल के लिए, ड्राइवर को एक ही ड्राइवर में शामिल करने की कोशिश करें. उदाहरण के लिए, अलग-अलग ड्राइवर रजिस्टर करने के बजाय, compatible स्ट्रिंग या डिवाइस के ऑक्ज़िलरी डेटा का इस्तेमाल करके अंतर किया जा सकता है. इसके अलावा, ड्राइवर मॉड्यूल को दो मॉड्यूल में बांटा जा सकता है.

Init और exit फ़ंक्शन के अपवाद

लाइब्रेरी मॉड्यूल, ड्राइवर रजिस्टर नहीं करते. इसलिए, इन पर module_init() और module_exit() से जुड़ी पाबंदियां लागू नहीं होतीं. ऐसा इसलिए, क्योंकि इन्हें डेटा स्ट्रक्चर, वर्क क्यू या कर्नेल थ्रेड सेट अप करने के लिए इन फ़ंक्शन की ज़रूरत पड़ सकती है.

`MODULE_DEVICE_TABLE` मैक्रो का इस्तेमाल करना

ड्राइवर मॉड्यूल में MODULE_DEVICE_TABLE मैक्रो शामिल होना चाहिए. इससे, मॉड्यूल लोड करने से पहले, उपयोगकर्ता स्पेस को यह पता चल जाता है कि ड्राइवर मॉड्यूल किन डिवाइसों के साथ काम करता है. Android इस डेटा का इस्तेमाल करके, मॉड्यूल लोड करने की प्रोसेस को ऑप्टिमाइज़ कर सकता है. जैसे, सिस्टम में मौजूद नहीं होने वाले डिवाइसों के लिए मॉड्यूल लोड करने से बचना. मैक्रो का इस्तेमाल करने के उदाहरण देखने के लिए, अपस्ट्रीम कोड देखें.

फ़ॉरवर्ड-डिक्लेयर किए गए डेटा टाइप की वजह से, सीआरसी के मेल न खाने की समस्या से बचना

फ़ॉरवर्ड-डिक्लेयर किए गए डेटा टाइप की जानकारी पाने के लिए, हेडर फ़ाइलें शामिल न करें. किसी हेडर फ़ाइल (header-A.h) में तय किए गए कुछ स्ट्रक्चर, यूनियन, और अन्य डेटा टाइप को किसी दूसरी हेडर फ़ाइल (header-B.h) में फ़ॉरवर्ड-डिक्लेयर किया जा सकता है. आम तौर पर, यह फ़ाइल उन डेटा टाइप के लिए पॉइंटर का इस्तेमाल करती है. इस कोड पैटर्न का मतलब है कि कर्नेल, जान-बूझकर header-B.h के उपयोगकर्ताओं के लिए डेटा स्ट्रक्चर को निजी रखने की कोशिश कर रहा है.

header-B.h के उपयोगकर्ताओं को, फ़ॉरवर्ड-डिक्लेयर किए गए इन डेटा स्ट्रक्चर के इंटरनल को सीधे तौर पर ऐक्सेस करने के लिए, header-A.h को शामिल नहीं करना चाहिए. ऐसा करने पर, CONFIG_MODVERSIONS सीआरसी के मेल न खाने की समस्याएं होती हैं. इससे, ABI के साथ काम करने से जुड़ी समस्याएं होती हैं. ऐसा तब होता है, जब कोई दूसरा कर्नेल (जैसे, GKI कर्नेल) मॉड्यूल लोड करने की कोशिश करता है.

उदाहरण के लिए, struct fwnode_handle को include/linux/fwnode.h में तय किया गया है. हालांकि, इसे include/linux/device.h में struct fwnode_handle; के तौर पर फ़ॉरवर्ड-डिक्लेयर किया गया है. ऐसा इसलिए, क्योंकि कर्नेल, include/linux/device.h के उपयोगकर्ताओं के लिए struct fwnode_handle की जानकारी को निजी रखने की कोशिश कर रहा है. इस स्थिति में, किसी मॉड्यूल में #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() में इनपुट के तौर पर पास करता है. इसके बाद, the cpufreq ड्राइवर मॉड्यूल को struct cpufreq_driver में सीधे तौर पर बदलाव नहीं करना चाहिए क्योंकि cpufreq_register_driver() को कॉल करने से, struct cpufreq_driver कर्नेल के लिए दिखने लगता है.

  • डेटा स्ट्रक्चर को आपके मॉड्यूल से शुरू नहीं किया गया हो. उदाहरण के लिए, regulator_register() से मिला struct regulator_dev.

कोर कर्नेल डेटा स्ट्रक्चर को सिर्फ़ कर्नेल से एक्सपोर्ट किए गए फ़ंक्शन या वेंडर हुक में साफ़ तौर पर इनपुट के तौर पर पास किए गए पैरामीटर के ज़रिए ही ऐक्सेस करें. अगर आपके पास कोर कर्नेल डेटा स्ट्रक्चर के कुछ हिस्सों में बदलाव करने के लिए कोई एपीआई या वेंडर हुक नहीं है, तो शायद ऐसा जान-बूझकर किया गया है. इसलिए, आपको मॉड्यूल से डेटा स्ट्रक्चर में बदलाव नहीं करना चाहिए. उदाहरण के लिए, 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().

`compatible` प्रॉपर्टी वाले डिवाइसट्री नोड को पार्स न करना

अगर किसी डिवाइस ट्री (डीटी) नोड में compatible प्रॉपर्टी है, तो उसके लिए struct device अपने-आप बांटा जाता है. इसके अलावा, यह तब भी बांटा जाता है, जब पैरंट डीटी नोड पर of_platform_populate() को कॉल किया जाता है. आम तौर पर, यह कॉल पैरंट डिवाइस के डिवाइस ड्राइवर से किया जाता है. डिफ़ॉल्ट तौर पर, यह माना जाता है कि compatible प्रॉपर्टी वाले डीटी नोड में struct device और उससे मेल खाने वाला डिवाइस ड्राइवर होता है. हालांकि, कुछ डिवाइसों को शेड्यूलर के लिए पहले ही शुरू कर दिया जाता है. अन्य सभी अपवादों को अपस्ट्रीम कोड से पहले ही हैंडल कर लिया जाता है.

इसके अलावा, fw_devlink (जिसे पहले of_devlink कहा जाता था) compatible प्रॉपर्टी वाले डीटी नोड को, ऐसे डिवाइस मानता है जिनके लिए struct device बांटा गया है. इसकी जांच ड्राइवर करता है. अगर किसी डीटी नोड में compatible प्रॉपर्टी है, लेकिन बांटे गए struct device की जांच नहीं की जाती है, तो fw_devlink, इसके कंज्यूमर डिवाइसों को जांच करने से रोक सकता है. इसके अलावा, सप्लायर डिवाइसों के लिए sync_state() कॉल को ब्लॉक कर सकता है.

अगर आपका ड्राइवर, of_find_*() फ़ंक्शन (जैसे, of_find_node_by_name() या of_find_compatible_node()) का इस्तेमाल करके, सीधे तौर पर ऐसे डीटी नोड को ढूंढता है जिसमें compatible प्रॉपर्टी है और फिर उस डीटी नोड को पार्स करता है, तो डिवाइस ड्राइवर लिखकर मॉड्यूल को ठीक करें. यह ड्राइवर, डिवाइस की जांच कर सकता है. इसके अलावा, compatible प्रॉपर्टी को हटाया जा सकता है. हालांकि, ऐसा सिर्फ़ तब किया जा सकता है, जब इसे अपस्ट्रीम न किया गया हो. अन्य विकल्पों पर चर्चा करने के लिए, Android कर्नेल टीम से kernel-team@android.com पर संपर्क करें. साथ ही, अपने इस्तेमाल के उदाहरणों को सही ठहराने के लिए तैयार रहें.

सप्लायर ढूंढने के लिए, डीटी फ़ैंडल का इस्तेमाल करना

जब भी मुमकिन हो, डीटी में फ़ैंडल (डीटी नोड का रेफ़रंस या पॉइंटर) का इस्तेमाल करके, किसी सप्लायर को रेफ़र करें. सप्लायर को रेफ़र करने के लिए, स्टैंडर्ड डीटी बाइंडिंग और फ़ैंडल का इस्तेमाल करने से, fw_devlink (जिसे पहले of_devlink कहा जाता था) रनटाइम पर डीटी को पार्स करके, डिवाइसों के बीच की निर्भरता अपने-आप तय कर लेता है. इसके बाद, कर्नेल डिवाइसों की जांच सही क्रम में अपने-आप कर सकता है. इससे, मॉड्यूल लोड करने के क्रम या MODULE_SOFTDEP() की ज़रूरत नहीं पड़ती.

लेगसी परिदृश्य (एआरएम कर्नेल में डीटी की सुविधा नहीं है)

पहले, एआरएम कर्नेल में डीटी की सुविधा जोड़ने से पहले, टच डिवाइस जैसे कंज्यूमर, रेगुलेटर जैसे सप्लायर को ढूंढने के लिए, ग्लोबल तौर पर यूनीक स्ट्रिंग का इस्तेमाल करते थे. उदाहरण के लिए, ACME PMIC ड्राइवर, एक से ज़्यादा रेगुलेटर (जैसे, acme-pmic-ldo1 से acme-pmic-ldo10) रजिस्टर या विज्ञापन कर सकता है. साथ ही, टच ड्राइवर , regulator_get(dev, "acme-pmic-ldo10") का इस्तेमाल करके, रेगुलेटर को ढूंढ सकता है. हालांकि, किसी दूसरे बोर्ड पर, LDO8, टच डिवाइस को सप्लाई कर सकता है. इससे एक मुश्किल सिस्टम बन जाता है, जहां एक ही टच ड्राइवर को हर उस बोर्ड के लिए रेगुलेटर की सही लुक-अप स्ट्रिंग तय करनी होती है जिस पर टच डिवाइस का इस्तेमाल किया जाता है.

मौजूदा परिदृश्य (एआरएम कर्नेल में डीटी की सुविधा है)

एआरएम कर्नेल में डीटी की सुविधा जोड़ने के बाद, कंज्यूमर, डीटी में सप्लायर की पहचान कर सकते हैं. इसके लिए, वे फ़ैंडल का इस्तेमाल करके, सप्लायर के डिवाइस ट्री नोड को रेफ़र करते हैं. कंज्यूमर, संसाधन का नाम इस आधार पर भी रख सकते हैं कि उसका इस्तेमाल किस लिए किया जाता है. इसके बजाय, यह नाम इस आधार पर भी रखा जा सकता है कि उसे कौन सप्लाई करता है. उदाहरण के लिए, पिछले उदाहरण में मौजूद टच ड्राइवर, regulator_get(dev, "core") और regulator_get(dev, "sensor") का इस्तेमाल करके, उन सप्लायर को पा सकता है जो टच डिवाइस के कोर और सेंसर को पावर देते हैं. ऐसे डिवाइस के लिए, उससे जुड़ा डीटी, कोड सैंपल के इस नमूने जैसा होता है:

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

दोनों दुनिया में सबसे खराब परिदृश्य

पुराने कर्नेल से पोर्ट किए गए कुछ ड्राइवर में, डीटी में लेगसी व्यवहार शामिल होता है. यह लेगसी स्कीम का सबसे खराब हिस्सा लेता है और उसे नई स्कीम पर लागू करता है. नई स्कीम का मकसद, चीज़ों को आसान बनाना है. ऐसे ड्राइवर में, कंज्यूमर ड्राइवर, डिवाइस के हिसाब से तय की गई डीटी प्रॉपर्टी का इस्तेमाल करके, लुकअप के लिए इस्तेमाल की जाने वाली स्ट्रिंग को पढ़ता है. सप्लायर, सप्लायर के हिसाब से तय की गई किसी दूसरी प्रॉपर्टी का इस्तेमाल करके, सप्लायर संसाधन को रजिस्टर करने के लिए इस्तेमाल किया जाने वाला नाम तय करता है. इसके बाद, कंज्यूमर और सप्लायर, सप्लायर को ढूंढने के लिए स्ट्रिंग का इस्तेमाल करने की पुरानी स्कीम का इस्तेमाल जारी रखते हैं. दोनों दुनिया में सबसे खराब परिदृश्य में:

  • टच ड्राइवर, इस कोड जैसा कोड इस्तेमाल करता है:

    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);
    
  • डीटी, इस कोड जैसा कोड इस्तेमाल करता है:

    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 को गड़बड़ी के तौर पर दिखाते हैं. इससे यह पता चलता है कि कोई डिवाइस, जांच करने की कोशिश कर रहा है, लेकिन फ़िलहाल ऐसा नहीं कर सकता. साथ ही, कर्नेल को बाद में फिर से जांच करनी चाहिए. यह पक्का करने के लिए कि ऐसे मामलों में, आपके डिवाइस का .probe() फ़ंक्शन उम्मीद के मुताबिक काम न करे, गड़बड़ी की वैल्यू को बदलें या रीमैप न करें. गड़बड़ी की वैल्यू को बदलने या रीमैप करने से, -EPROBE_DEFER को हटाया जा सकता है. इससे, आपके डिवाइस की कभी जांच नहीं हो पाएगी.

`devm_*()` एपीआई के वैरिएंट का इस्तेमाल करना

जब डिवाइस, devm_*() एपीआई का इस्तेमाल करके कोई संसाधन हासिल करता है, तो डिवाइस की जांच में गड़बड़ी होने पर, कर्नेल उस संसाधन को अपने-आप रिलीज़ कर देता है. इसके अलावा, अगर डिवाइस की जांच सही तरीके से हो जाती है और बाद में उसे अनबाउंड कर दिया जाता है, तब भी कर्नेल उस संसाधन को अपने-आप रिलीज़ कर देता है. इस सुविधा से, probe() फ़ंक्शन में गड़बड़ी को हैंडल करने वाला कोड साफ़-सुथरा हो जाता है, क्योंकि इसमें devm_*() से हासिल किए गए संसाधनों को रिलीज़ करने के लिए, goto जंप की ज़रूरत नहीं होती. साथ ही, ड्राइवर को अनबाउंड करने की कार्रवाइयां आसान हो जाती हैं.

डिवाइस-ड्राइवर को अनबाउंड करने की प्रोसेस को हैंडल करना

डिवाइस ड्राइवर को अनबाउंड करने के बारे में सोच-समझकर फ़ैसला लें. साथ ही, अनबाउंड करने की प्रोसेस को तय न करें, क्योंकि तय न करने का मतलब यह नहीं है कि इसकी अनुमति नहीं है. आपको डिवाइस-ड्राइवर को अनबाउंड करने की प्रोसेस को पूरी तरह से लागू करना होगा या साफ़ तौर पर डिवाइस-ड्राइवर को अनबाउंड करने की प्रोसेस को बंद करना होगा.

डिवाइस-ड्राइवर को अनबाउंड करने की प्रोसेस को लागू करना

डिवाइस-ड्राइवर को अनबाउंड करने की प्रोसेस को पूरी तरह से लागू करने का विकल्प चुनने पर, डिवाइस ड्राइवर को साफ़ तौर पर अनबाउंड करें. इससे, मेमोरी या संसाधन लीक होने और सुरक्षा से जुड़ी समस्याओं से बचा जा सकता है. किसी डिवाइस को ड्राइवर से बाइंड करने के लिए, ड्राइवर के probe() फ़ंक्शन को कॉल किया जा सकता है. साथ ही, किसी डिवाइस को अनबाउंड करने के लिए, ड्राइवर के remove() फ़ंक्शन को कॉल किया जा सकता है. अगर कोई remove() फ़ंक्शन मौजूद नहीं है, तो कर्नेल अब भी डिवाइस को अनबाउंड कर सकता है. ड्राइवर कोर यह मानता है कि डिवाइस से अनबाउंड होने पर, ड्राइवर को किसी भी तरह के क्लीन अप की ज़रूरत नहीं होती. किसी डिवाइस से अनबाउंड होने पर, ड्राइवर को साफ़ तौर पर किसी भी तरह का क्लीन अप करने की ज़रूरत नहीं होती. हालांकि, इसके लिए ये दोनों शर्तें पूरी होनी चाहिए:

  • ड्राइवर के probe() फ़ंक्शन से हासिल किए गए सभी संसाधन, devm_*() एपीआई के ज़रिए हासिल किए गए हों.

  • हार्डवेयर डिवाइस को शटडाउन या क्विसिंग सीक्वेंस की ज़रूरत न हो.

इस स्थिति में, ड्राइवर कोर, devm_*() एपीआई के ज़रिए हासिल किए गए सभी संसाधनों को रिलीज़ करने की प्रोसेस को हैंडल करता है. अगर ऊपर दिए गए दोनों में से कोई भी स्टेटमेंट सही नहीं है, तो डिवाइस से अनबाउंड होने पर, ड्राइवर को क्लीनअप करना होगा. जैसे, संसाधनों को रिलीज़ करना और हार्डवेयर को शट डाउन करें या क्विस करना. यह पक्का करने के लिए कि कोई डिवाइस, ड्राइवर मॉड्यूल को साफ़ तौर पर अनबाउंड कर सके, इनमें से कोई एक विकल्प इस्तेमाल करें:

  • अगर हार्डवेयर को शटडाउन या क्विसिंग सीक्वेंस की ज़रूरत नहीं है, तो डिवाइस मॉड्यूल को devm_*() एपीआई का इस्तेमाल करके संसाधन हासिल करने के लिए बदलें.

  • probe() फ़ंक्शन वाले स्ट्रक्चर में ही, remove() ड्राइवर ऑपरेशन लागू करें. इसके बाद, remove() फ़ंक्शन का इस्तेमाल करके, क्लीन अप के चरण पूरे करें.

साफ़ तौर पर डिवाइस-ड्राइवर को अनबाउंड करने की प्रोसेस को बंद करना (हम इसका सुझाव नहीं देते)

डिवाइस-ड्राइवर को अनबाउंड करने की प्रोसेस को साफ़ तौर पर बंद करने का विकल्प चुनने पर, आपको अनबाउंड करने की अनुमति और मॉड्यूल को अनलोड करने की अनुमति नहीं देनी होगी.

  • अनबाउंड करने की अनुमति न देने के लिए, ड्राइवर के struct device_driver में suppress_bind_attrs फ़्लैग को true पर सेट करें. इस सेटिंग से, ड्राइवर की sysfs डायरेक्ट्री में bind और unbind फ़ाइलें नहीं दिखेंगी. unbind फ़ाइल की मदद से, उपयोगकर्ता स्पेस, किसी ड्राइवर को उसके डिवाइस से अनबाउंड करने की प्रोसेस को ट्रिगर कर सकता है.

  • मॉड्यूल को अनलोड करने की अनुमति न देने के लिए, पक्का करें कि lsmod में मॉड्यूल के लिए [permanent] हो. module_exit() या module_XXX_driver() का इस्तेमाल न करने पर, मॉड्यूल को [permanent] के तौर पर मार्क किया जाता है.

जांच करने वाले फ़ंक्शन में फ़र्मवेयर लोड न करना

ड्राइवर को .probe() फ़ंक्शन में फ़र्मवेयर लोड नहीं करना चाहिए, क्योंकि हो सकता है कि फ़्लैश या परमानेंट स्टोरेज पर आधारित फ़ाइल सिस्टम माउंट होने से पहले, ड्राइवर की जांच करने पर, उनके पास फ़र्मवेयर का ऐक्सेस न हो. ऐसे मामलों में, request_firmware*() एपीआई, लंबे समय तक ब्लॉक हो सकता है और फिर काम नहीं कर सकता. इससे, बूट प्रोसेस की स्पीड कम हो सकती है. इसके बजाय, फ़र्मवेयर लोड करने की प्रोसेस को तब तक के लिए टाल दें, जब तक कोई क्लाइंट डिवाइस का इस्तेमाल शुरू न करे. उदाहरण के लिए, डिसप्ले ड्राइवर, डिसप्ले डिवाइस खुलने पर फ़र्मवेयर लोड कर सकता है.

कुछ मामलों में, फ़र्मवेयर लोड करने के लिए .probe() का इस्तेमाल करना ठीक हो सकता है. जैसे, क्लॉक ड्राइवर में फ़र्मवेयर की ज़रूरत होती है, लेकिन डिवाइस, उपयोगकर्ता स्पेस के लिए उपलब्ध नहीं होता. इस्तेमाल के अन्य सही उदाहरण भी हो सकते हैं.

एसिंक्रोनस जांच की सुविधा लागू करना

एसिंक्रोनस जांच की सुविधा का इस्तेमाल करें और इसे लागू करें. इससे, आने वाले समय में होने वाले सुधारों का फ़ायदा लिया जा सकेगा. जैसे, मॉड्यूल को पैरलल तरीके से लोड करना या डिवाइस की जांच करना. इससे, बूट टाइम को कम किया जा सकेगा. आने वाले समय में, Android के वर्शन में ये सुविधाएं जोड़ी जा सकती हैं. एसिंक्रोनस जांच की सुविधा का इस्तेमाल न करने वाले ड्राइवर मॉड्यूल, ऐसे ऑप्टिमाइज़ेशन की असरदारता को कम कर सकते हैं.

किसी ड्राइवर को एसिंक्रोनस जांच की सुविधा के साथ काम करने और इसे प्राथमिकता देने के तौर पर मार्क करने के लिए, ड्राइवर के struct device_driver सदस्य में probe_type फ़ील्ड सेट करें. यहां दिए गए उदाहरण में, प्लैटफ़ॉर्म ड्राइवर के लिए इस सुविधा को चालू किया गया है:

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

किसी ड्राइवर को एसिंक्रोनस जांच की सुविधा के साथ काम करने के लिए, किसी खास कोड की ज़रूरत नहीं होती. हालांकि, एसिंक्रोनस जांच की सुविधा जोड़ने के दौरान, इन बातों का ध्यान रखें.

  • पहले से जांच की गई डिपेंडेंसी के बारे में कोई अनुमान न लगाएं. सीधे तौर पर या घुमा-फिराकर (ज़्यादातर फ़्रेमवर्क कॉल) जांच करें. साथ ही, अगर एक या उससे ज़्यादा सप्लायर अब तक तैयार नहीं हैं, तो -EPROBE_DEFER दिखाएं.

  • अगर पैरंट डिवाइस के जांच करने वाले फ़ंक्शन में चाइल्ड डिवाइस जोड़े जाते हैं, तो यह न मान लें कि चाइल्ड डिवाइस की जांच तुरंत हो जाएगी.

  • अगर जांच में गड़बड़ी होती है, तो गड़बड़ी को हैंडल करने की सही प्रोसेस अपनाएं और क्लीन अप करें. इसके लिए, Use devm_*() एपीआई के वैरिएंट का इस्तेमाल करना लेख देखें.

डिवाइस की जांच के क्रम के लिए, MODULE_SOFTDEP का इस्तेमाल न करना

MODULE_SOFTDEP() फ़ंक्शन, डिवाइस की जांच के क्रम की गारंटी देने के लिए भरोसेमंद समाधान नहीं है. इसलिए, इसका इस्तेमाल इन वजहों से नहीं किया जाना चाहिए.

  • जांच को टालना. जब कोई मॉड्यूल लोड होता है, तो डिवाइस की जांच को टाला जा सकता है, क्योंकि उसके किसी सप्लायर की जांच नहीं की गई है. इससे, मॉड्यूल लोड करने के क्रम और डिवाइस की जांच के क्रम में अंतर हो सकता है.

  • एक ड्राइवर, कई डिवाइस. कोई ड्राइवर मॉड्यूल, किसी खास तरह के डिवाइस को मैनेज कर सकता है. अगर सिस्टम में किसी डिवाइस टाइप के एक से ज़्यादा इंस्टेंस शामिल हैं और उन डिवाइसों के लिए जांच का क्रम अलग-अलग है, तो मॉड्यूल लोड करने के क्रम का इस्तेमाल करके, उन ज़रूरतों को पूरा नहीं किया जा सकता.

  • एसिंक्रोनस जांच. एसिंक्रोनस जांच करने वाले ड्राइवर मॉड्यूल, मॉड्यूल लोड होने पर तुरंत किसी डिवाइस की जांच नहीं करते. इसके बजाय, डिवाइस की जांच को पैरलल थ्रेड हैंडल करता है. इससे, मॉड्यूल लोड करने के क्रम और डिवाइस की जांच के क्रम में अंतर हो सकता है. उदाहरण के लिए, जब कोई I2C मेन ड्राइवर मॉड्यूल, एसिंक्रोनस जांच करता है और टच ड्राइवर मॉड्यूल, I2C बस पर मौजूद PMIC पर निर्भर करता है, तो टच ड्राइवर और PMIC ड्राइवर के सही क्रम में लोड होने पर भी, PMIC ड्राइवर की जांच से पहले, टच ड्राइवर की जांच की जा सकती है.

अगर आपके पास MODULE_SOFTDEP() फ़ंक्शन का इस्तेमाल करने वाले ड्राइवर मॉड्यूल हैं, तो उन्हें ठीक करें, ताकि वे उस फ़ंक्शन का इस्तेमाल न करें. आपकी मदद करने के लिए, Android टीम ने ऐसे बदलाव किए हैं जिन्हें अपस्ट्रीम किया गया है. इससे, कर्नेल, MODULE_SOFTDEP() का इस्तेमाल किए बिना, क्रम से जुड़ी समस्याओं को हैंडल कर सकता है. खास तौर पर, जांच के क्रम को पक्का करने के लिए, fw_devlink का इस्तेमाल किया जा सकता है. साथ ही, किसी डिवाइस के सभी कंज्यूमर की जांच हो जाने के बाद, ज़रूरी टास्क पूरे करने के लिए, sync_state() कॉलबैक का इस्तेमाल किया जा सकता है.

कॉन्फ़िगरेशन के लिए, #ifdef के बजाय #if IS_ENABLED() का इस्तेमाल करना

यह पक्का करने के लिए कि अगर आने वाले समय में कॉन्फ़िगरेशन, ट्राइस्टेट कॉन्फ़िगरेशन में बदलता है, तो #if ब्लॉक में मौजूद कोड कंपाइल होता रहे, #ifdef CONFIG_XXX के बजाय #if IS_ENABLED(CONFIG_XXX) का इस्तेमाल करें. इन दोनों के बीच के फ़र्क़ यहां दिए गए हैं:

  • #if IS_ENABLED(CONFIG_XXX) को मॉड्यूल (=m) या बिल्ट-इन (=y) पर सेट करने पर, true की वैल्यू CONFIG_XXX होती है.

  • #ifdef CONFIG_XXX की वैल्यू true होती है, जब CONFIG_XXX को बिल्ट-इन (=y) पर सेट किया जाता है. हालांकि, जब CONFIG_XXX को मॉड्यूल (=m) पर सेट किया जाता है, तब इसकी वैल्यू true नहीं होती. इसका इस्तेमाल सिर्फ़ तब करें जब आपको पक्का हो कि कॉन्फ़िगरेशन को मॉड्यूल पर सेट करने या बंद करने पर, आपको एक ही काम करना है.

शर्त के हिसाब से कंपाइल करने के लिए, सही मैक्रो का इस्तेमाल करना

अगर एक CONFIG_XXX को मॉड्यूल (=m) पर सेट किया जाता है, तो बिल्ड सिस्टम अपने-आप CONFIG_XXX_MODULE को तय करता है. अगर आपका ड्राइवर, CONFIG_XXX से कंट्रोल होता है और आपको यह देखना है कि आपका ड्राइवर, मॉड्यूल के तौर पर कंपाइल हो रहा है या नहीं, तो इन दिशा-निर्देशों का पालन करें:

  • अपने ड्राइवर की सी फ़ाइल (या कोई भी सोर्स फ़ाइल जो हेडर फ़ाइल नहीं है) में, #ifdef CONFIG_XXX_MODULE का इस्तेमाल न करें, क्योंकि यह ज़रूरत से ज़्यादा पाबंदी लगाने वाला है. साथ ही, अगर कॉन्फ़िगरेशन का नाम बदलकर CONFIG_XYZ कर दिया जाता है, तो यह काम नहीं करता. किसी भी ऐसी नॉन-हेडर सोर्स फ़ाइल के लिए जिसे मॉड्यूल में कंपाइल किया जाता है, बिल्ड सिस्टम, उस फ़ाइल के स्कोप के लिए MODULE को अपने-आप तय करता है. इसलिए, यह देखने के लिए कि कोई सी फ़ाइल (या कोई भी नॉन-हेडर सोर्स फ़ाइल) मॉड्यूल के तौर पर कंपाइल हो रही है या नहीं, CONFIG_ प्रीफ़िक्स के बिना #ifdef MODULE का इस्तेमाल करें.

  • हेडर फ़ाइलों में, इसी जांच को करना मुश्किल होता है, क्योंकि हेडर फ़ाइलें सीधे तौर पर बाइनरी में कंपाइल नहीं होतीं. इसके बजाय, इन्हें सी फ़ाइल (या अन्य सोर्स फ़ाइलों) के तौर पर कंपाइल किया जाता है. हेडर फ़ाइलों के लिए, इन नियमों का इस्तेमाल करें:

    • #ifdef MODULE का इस्तेमाल करने वाली हेडर फ़ाइल के लिए, नतीजा इस आधार पर बदलता है कि कौनसी सोर्स फ़ाइल इसका इस्तेमाल कर रही है. इसका मतलब है कि एक ही बिल्ड में मौजूद एक ही हेडर फ़ाइल के कोड के अलग-अलग हिस्सों को, अलग-अलग सोर्स फ़ाइलों (मॉड्यूल बनाम बिल्ट-इन या बंद) के लिए कंपाइल किया जा सकता है. यह तब काम का हो सकता है, जब आपको कोई ऐसा मैक्रो तय करना हो जिसे बिल्ट-इन कोड के लिए एक तरीके से और मॉड्यूल के लिए किसी दूसरे तरीके से एक्सपैंड करना हो.

    • किसी हेडर फ़ाइल को कोड के किसी हिस्से में कंपाइल करने के लिए, जब कोई खास CONFIG_XXX मॉड्यूल पर सेट हो (चाहे इसे शामिल करने वाली सोर्स फ़ाइल कोई मॉड्यूल हो या न हो), तो हेडर फ़ाइल को #ifdef CONFIG_XXX_MODULE का इस्तेमाल करना होगा.