अपने वेंडर मॉड्यूल को ज़्यादा भरोसेमंद और मज़बूत बनाने के लिए, इन दिशा-निर्देशों का पालन करें. कई दिशा-निर्देशों का पालन करने से, सही मॉड्यूल लोड ऑर्डर और डिवाइसों की जांच करने के लिए ड्राइवर के ऑर्डर का पता लगाना आसान हो जाता है.
कोई मॉड्यूल, लाइब्रेरी या ड्राइवर हो सकता है.
लाइब्रेरी मॉड्यूल ऐसी लाइब्रेरी होती हैं जो अन्य मॉड्यूल को इस्तेमाल करने के लिए एपीआई उपलब्ध कराती हैं. इस तरह के मॉड्यूल, आम तौर पर किसी खास हार्डवेयर के लिए नहीं होते. लाइब्रेरी मॉड्यूल के उदाहरणों में, एईएस एन्क्रिप्शन मॉड्यूल,
remoteprocफ़्रेमवर्क (जिसे मॉड्यूल के तौर पर कंपाइल किया जाता है), और लॉगबफ़र मॉड्यूल शामिल हैं.module_init()में मौजूद मॉड्यूल कोड, डेटा स्ट्रक्चर सेट अप करने के लिए चलता है. हालांकि, कोई और कोड तब तक नहीं चलता, जब तक उसे किसी बाहरी मॉड्यूल से ट्रिगर न किया जाए.ड्राइवर मॉड्यूल ऐसे ड्राइवर होते हैं जो किसी खास तरह के डिवाइस की जांच करते हैं या उससे जुड़ते हैं. इस तरह के मॉड्यूल, हार्डवेयर के हिसाब से होते हैं. ड्राइवर मॉड्यूल के उदाहरणों में यूएआरटी, पीसीआईई, और वीडियो एन्कोडर हार्डवेयर शामिल हैं. ड्राइवर मॉड्यूल सिर्फ़ तब चालू होते हैं, जब उनसे जुड़ा डिवाइस सिस्टम पर मौजूद हो.
अगर डिवाइस मौजूद नहीं है, तो सिर्फ़ यह मॉड्यूल कोड चलता है:
module_init()यह कोड, ड्राइवर को ड्राइवर कोर फ़्रेमवर्क के साथ रजिस्टर करता है.अगर डिवाइस मौजूद है और ड्राइवर उस डिवाइस की जांच करता है या उससे जुड़ जाता है, तो अन्य मॉड्यूल कोड चल सकता है.
मॉड्यूल को शुरू और बंद करने के लिए सही तरीके से फ़ंक्शन का इस्तेमाल करना
ड्राइवर मॉड्यूल को module_init() में ड्राइवर को रजिस्टर करना होगा और 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 को शामिल नहीं करना चाहिए. ऐसा करने पर, जब कोई दूसरा कर्नल (जैसे कि GKI कर्नल) मॉड्यूल लोड करने की कोशिश करता है, तो CONFIG_MODVERSIONS CRC
mismatch की समस्याएं होती हैं. इससे ABI के नियमों का पालन न करने से जुड़ी समस्याएं जनरेट होती हैं.
उदाहरण के लिए, struct fwnode_handle को include/linux/fwnode.h में तय किया गया है, लेकिन इसे include/linux/device.h में struct fwnode_handle; के तौर पर फ़ॉरवर्ड किया गया है. ऐसा इसलिए, क्योंकि कर्नल include/linux/device.h के उपयोगकर्ताओं से struct fwnode_handle की जानकारी को निजी रखना चाहता है. इस स्थिति में, 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कर्नल को दिखता है.आपके मॉड्यूल ने डेटा स्ट्रक्चर को शुरू नहीं किया है. उदाहरण के लिए,
struct regulator_dev,regulator_register()से मिला है.
सिर्फ़ कर्नल से एक्सपोर्ट किए गए फ़ंक्शन या वेंडर हुक को इनपुट के तौर पर साफ़ तौर पर पास किए गए पैरामीटर के ज़रिए, मुख्य कर्नल डेटा स्ट्रक्चर को ऐक्सेस करें. अगर आपके पास कोर कर्नल डेटा स्ट्रक्चर के कुछ हिस्सों में बदलाव करने के लिए, एपीआई या वेंडर हुक नहीं है, तो हो सकता है कि ऐसा जान-बूझकर किया गया हो. आपको मॉड्यूल से डेटा स्ट्रक्चर में बदलाव नहीं करना चाहिए. उदाहरण के लिए, 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 प्रॉपर्टी है, तो उसके लिए struct device अपने-आप असाइन हो जाता है. इसके अलावा, पैरंट डीटी नोड पर of_platform_populate() कॉल करने पर भी struct device असाइन हो जाता है. आम तौर पर, पैरंट डिवाइस का डिवाइस ड्राइवर ऐसा करता है. डिफ़ॉल्ट रूप से यह माना जाता है कि compatible प्रॉपर्टी वाले DT नोड में struct device और उससे मेल खाने वाला डिवाइस ड्राइवर होता है. हालांकि, कुछ डिवाइसों के लिए यह नियम लागू नहीं होता. इन डिवाइसों को शेड्यूलर के लिए पहले से शुरू किया गया होता है. अन्य सभी अपवादों को अपस्ट्रीम कोड पहले ही हैंडल कर लेता है.
इसके अलावा, fw_devlink (जिसे पहले of_devlink कहा जाता था) compatible प्रॉपर्टी वाले डीटी नोड को ऐसे डिवाइस मानता है जिन्हें struct device असाइन किया गया है. इसकी जांच ड्राइवर करता है. अगर किसी डीटी नोड में compatible प्रॉपर्टी है, लेकिन असाइन किए गए struct device की जांच नहीं की गई है, तो fw_devlink, उपभोक्ता डिवाइसों को जांच करने से रोक सकता है. इसके अलावा, sync_state() कॉल को सप्लायर डिवाइसों के लिए कॉल किए जाने से भी रोक सकता है.
अगर आपका ड्राइवर, compatible प्रॉपर्टी वाले किसी डीटी नोड को सीधे तौर पर ढूंढने के लिए of_find_*() फ़ंक्शन (जैसे कि of_find_node_by_name()
या of_find_compatible_node()) का इस्तेमाल करता है और फिर उस डीटी नोड को पार्स करता है, तो डिवाइस ड्राइवर लिखकर मॉड्यूल को ठीक करें. यह ड्राइवर, डिवाइस की जांच कर सकता है या compatible प्रॉपर्टी को हटा सकता है. ऐसा सिर्फ़ तब किया जा सकता है, जब इसे अपस्ट्रीम न किया गया हो. अन्य विकल्पों के बारे में जानने के लिए, Android Kernel Team से kernel-team@android.com पर संपर्क करें. साथ ही, इस्तेमाल के उदाहरणों के बारे में बताने के लिए तैयार रहें.
सप्लायर ढूंढने के लिए, DT phandle का इस्तेमाल करना
जब भी हो सके, DT में किसी सप्लायर को रेफ़र करने के लिए, phandle (DT नोड का रेफ़रंस या पॉइंटर) का इस्तेमाल करें. सप्लायर के लिए स्टैंडर्ड डीटी बाइंडिंग और फ़ैंडल का इस्तेमाल करने से, fw_devlink (पहले of_devlink) को रनटाइम में डीटी पार्स करके, डिवाइसों के बीच की निर्भरता का अपने-आप पता लगाने में मदद मिलती है. इसके बाद, कर्नल सही क्रम में डिवाइसों की अपने-आप जांच कर सकता है. इससे मॉड्यूल लोड करने के क्रम या MODULE_SOFTDEP() की ज़रूरत नहीं होती.
लेगसी परिदृश्य (एआरएम कर्नल में डीटी का इस्तेमाल नहीं किया जा सकता)
पहले, एआरएम कर्नल में डीटी सपोर्ट जोड़ने से पहले, टच डिवाइस जैसे उपभोक्ता, ग्लोबल यूनीक स्ट्रिंग का इस्तेमाल करके, रेगुलेटर जैसे सप्लायर को ढूंढते थे.
उदाहरण के लिए, ACME PMIC ड्राइवर, कई रेगुलेटर (जैसे कि acme-pmic-ldo1 से acme-pmic-ldo10) को रजिस्टर या उनका विज्ञापन कर सकता है. साथ ही, टच ड्राइवर, regulator_get(dev, "acme-pmic-ldo10") का इस्तेमाल करके किसी रेगुलेटर को ढूंढ सकता है.
हालांकि, किसी दूसरे बोर्ड पर LDO8, टच डिवाइस को पावर सप्लाई कर सकता है. इससे एक ऐसा सिस्टम बन जाता है जिसमें एक ही टच ड्राइवर को, हर उस बोर्ड के लिए रेगुलेटर की सही लुक-अप स्ट्रिंग तय करनी होती है जिस पर टच डिवाइस का इस्तेमाल किया जाता है.
मौजूदा स्थिति (एआरएम कर्नल में डीटी सपोर्ट)
ARM कर्नल में DT की सुविधा जोड़ने के बाद, उपभोक्ता 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 में लेगसी बिहेवियर शामिल है. यह लेगसी स्कीम का सबसे खराब हिस्सा लेता है और इसे नई स्कीम पर लागू करता है. नई स्कीम को चीज़ों को आसान बनाने के लिए डिज़ाइन किया गया है. ऐसे ड्राइवर में, उपभोक्ता ड्राइवर, डिवाइस के हिसाब से डीटी प्रॉपर्टी का इस्तेमाल करके, लुकअप के लिए स्ट्रिंग को पढ़ता है. इसके बाद, सप्लायर, सप्लायर के हिसाब से दूसरी प्रॉपर्टी का इस्तेमाल करके, सप्लायर के संसाधन को रजिस्टर करने के लिए इस्तेमाल किए जाने वाले नाम को तय करता है. इसके बाद, उपभोक्ता और सप्लायर, सप्लायर को खोजने के लिए स्ट्रिंग का इस्तेमाल करने की पुरानी स्कीम का इस्तेमाल करते रहते हैं. इस स्थिति में:
टच ड्राइवर, इस कोड की तरह का कोड इस्तेमाल करता है:
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_*()एपीआई का इस्तेमाल करके संसाधन पाने के लिए, डिवाइस मॉड्यूल बदलें.remove()फ़ंक्शन के साथ-साथ,remove()ड्राइवर ऑपरेशन को एक ही स्ट्रक्चर में लागू करें. इसके बाद,remove()फ़ंक्शन का इस्तेमाल करके, क्लीन अप करने के चरण पूरे करें.probe()
डिवाइस-ड्राइवर को अनबाइंड करने की सुविधा को साफ़ तौर पर बंद करें (हम इसका सुझाव नहीं देते)
डिवाइस-ड्राइवर को अनबाइंड करने की सुविधा को साफ़ तौर पर बंद करने का विकल्प चुनने पर, आपको अनबाइंड करने की अनुमति और मॉड्यूल को अनलोड करने की अनुमति नहीं देनी होगी.
अनबाइंड करने की अनुमति न देने के लिए, ड्राइवर के
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,
},
};
एसिंक्रोनस प्रोबिंग के साथ काम करने के लिए, ड्राइवर को किसी खास कोड की ज़रूरत नहीं होती. हालांकि, असिंक्रोनस प्रोबिंग की सुविधा जोड़ते समय इन बातों का ध्यान रखें.
पहले से जांची गई डिपेंडेंसी के बारे में अनुमान न लगाएं. सीधे तौर पर या अप्रत्यक्ष रूप से (ज़्यादातर फ़्रेमवर्क कॉल) जांच करें. अगर एक या उससे ज़्यादा सप्लायर अभी तक तैयार नहीं हैं, तो
-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का इस्तेमाल करना होगा.