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

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

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

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