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

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

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

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

इनिट और एग्ज़िट फ़ंक्शन के अपवाद

लाइब्रेरी मॉड्यूल, ड्राइवर रजिस्टर नहीं करते. साथ ही, इन पर 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 CRC के मैच न होने की समस्याएं आती हैं. इन समस्याओं की वजह से, एबीआई के अनुपालन से जुड़ी समस्याएं भी आती हैं. ये समस्याएं तब आती हैं, जब कोई दूसरा कर्नेल (जैसे, GKI कर्नेल) मॉड्यूल को लोड करने की कोशिश करता है.

उदाहरण के लिए, struct fwnode_handle को include/linux/fwnode.h में परिभाषित किया गया है, लेकिन इसे include/linux/device.h में struct fwnode_handle; के तौर पर फ़ॉरवर्ड डिक्लेयर्ड किया गया है. ऐसा इसलिए किया गया है, क्योंकि कर्नेल struct fwnode_handle की जानकारी को include/linux/device.h के उपयोगकर्ताओं से निजी रखने की कोशिश कर रहा है. इस स्थिति में, struct fwnode_handle के सदस्यों का ऐक्सेस पाने के लिए, किसी मॉड्यूल में #include <linux/fwnode.h> न जोड़ें. जिस डिज़ाइन में आपको ऐसी हेडर फ़ाइलें शामिल करनी हैं उसका डिज़ाइन पैटर्न खराब होने का मतलब है.

सीधे तौर पर कोर कर्नेल स्ट्रक्चर को ऐक्सेस न करें

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

  • डेटा स्ट्रक्चर के बारे में जानकारी, 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 कर्नेल में दिखता है.

  • आपके मॉड्यूल से डेटा स्ट्रक्चर शुरू नहीं होता है. उदाहरण के लिए, 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().

काम करने वाली प्रॉपर्टी के साथ डिवाइस ट्री नोड को पार्स न करें

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

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

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

सप्लायर खोजने के लिए, DT phandle का इस्तेमाल करना

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

लेगसी स्थिति (ARM कर्नेल में DT की सुविधा उपलब्ध नहीं है)

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

मौजूदा स्थिति (ARM कर्नेल में DT सहायता)

ARM कर्नेल में DT सहायता जोड़े जाने के बाद, उपभोक्ता phandle का इस्तेमाल करके सप्लायर के डिवाइस ट्री नोड का हवाला देकर डीटी में सप्लायर की पहचान कर सकते हैं. उपभोक्ता, रिसोर्स को इस आधार पर भी नाम दे सकते हैं कि उसका इस्तेमाल किस काम के लिए किया जाता है, न कि इसे किसने उपलब्ध कराया है. उदाहरण के लिए, पिछले उदाहरण में दिए गए टच ड्राइवर में 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 {
        ...
    };
};

दोनों के लिए सबसे खराब स्थिति

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

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

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

डिवाइस-ड्राइवर अनबाइंड करने की प्रोसेस को मैनेज करना

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

डिवाइस-ड्राइवर अनबाइंड करने की सुविधा लागू करना

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

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

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

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

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

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

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

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

  • अनबाइंड करने की अनुमति न देने के लिए, ड्राइवर के struct device_driver में suppress_bind_attrs फ़्लैग को true पर सेट करें. इससे bind और unbind फ़ाइलें, ड्राइवर की sysfs डायरेक्ट्री में नहीं दिखेंगी. 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,
        },
};

एसिंक्रोनस प्रोबिंग के साथ ड्राइवर काम करने के लिए विशेष कोड की ज़रूरत नहीं होती है. हालांकि, असाइनमेंट के लिए, एक साथ कई प्रोसेस चलाने की सुविधा जोड़ते समय इन बातों का ध्यान रखें.

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

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

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

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

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

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

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

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

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

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

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

  • CONFIG_XXX को मॉड्यूल (=m) या पहले से मौजूद (=y) पर सेट करने पर, #if IS_ENABLED(CONFIG_XXX) का आकलन true के तौर पर किया जाता है.

  • CONFIG_XXX को बिल्ट-इन (=y) पर सेट करने पर, #ifdef CONFIG_XXX का आकलन 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 का इस्तेमाल करना होगा.