डिवाइस ट्री क्या है?
August 7, 2026 · – विज़िटर
अगर आपने कभी किसी एम्बेडेड बोर्ड पर लिनक्स चलाने की कोशिश की है, तो देर-सवेर .dts से ख़त्म होने वाली एक फ़ाइल से सामना होता ही है। और आमतौर पर सबसे बुरे वक़्त पर: बोर्ड बूट नहीं हो रहा, स्क्रीन पर कुछ नहीं आ रहा, और कोई कहता है "अपना डिवाइस ट्री देखो"। यह लेख उसी फ़ाइल के बारे में है — वह क्या है, क्यों है, और उसमें आप दरअसल क्या ढूँढ़ रहे हैं।
यह किस समस्या को हल करता है
डेस्कटॉप पर कर्नेल हार्डवेयर काफ़ी हद तक ख़ुद ढूँढ़ लेता है। PCI और USB औपचारिक रूप से निर्दिष्ट और गणनीय बस हैं (1)। डिवाइस अपने वेंडर और प्रोडक्ट आईडी के साथ ख़ुद को बताते हैं; ड्राइवर जिन आईडी को सपोर्ट करता है उन्हें एक तालिका में सूचीबद्ध करता है, और कर्नेल हर मेल खाने वाले डिवाइस के लिए उस ड्राइवर का probe() बुलाता है (2)। USB की तरफ़ भी यही काम उसकी अपनी आईडी तालिका से होता है (3)। किसी को पहले से किसी को कुछ बताने की ज़रूरत नहीं पड़ती।
एम्बेडेड बोर्डों को यह सुविधा नहीं मिलती। कर्नेल का अपना दस्तावेज़ यह रेखा साफ़ खींचता है: सिस्टम-ऑन-चिप में एकीकृत पेरिफेरल न्यूनतम ढाँचे वाली बसों से लटके होते हैं — "PCI या USB जैसी बड़ी, औपचारिक रूप से निर्दिष्ट बसों के विपरीत" — और इन डिवाइसों में समानता यह है कि इन्हें सीपीयू बस से सीधे एड्रेस किया जाता है (1)। कोई I²C सेंसर या SPI से लगा डिस्प्ले कंट्रोलर ख़ुद को नहीं बताता; वह बस वहाँ, उसी पते पर, उसी इंटरप्ट लाइन से जुड़ा बैठा रहता है। कर्नेल को इसका पता चलने का एकमात्र तरीक़ा यह है कि कोई उसे बताए।
लंबे समय तक वह "कोई" ख़ुद कोड ही था। हर बोर्ड के लिए कर्नेल ट्री में एक फ़ाइल होती थी जो C में बताती थी कि कौन-सा डिवाइस कहाँ जुड़ा है — कर्नेल आज भी इस रास्ते को दर्ज करता है, जिसमें बोर्ड-विशिष्ट सेटअप कोड डिवाइसों को एक-एक करके पंजीकृत करता है (1)। ARM पर ये फ़ाइलें इतनी बढ़ गईं कि 2011 में लिनुस टोरवाल्ड्स ने एक ईमेल में पूरी स्थिति की कड़ी चीरफाड़ की और कहा कि ARM कोड ऐसी दिशा में जा रहा है जो टिकाऊ नहीं है। जो रास्ता अपनाया गया, वह डिवाइस ट्री था।
विचार
डिवाइस ट्री एक डेटा फ़ाइल है जो बताती है कि हार्डवेयर कैसे जुड़ा है। यह कोड नहीं है — न चलता है, न कर्नेल के भीतर संकलित होता है। यह एक विवरण है: इस पते पर एक कंट्रोलर है, वह यह इंटरप्ट लाइन इस्तेमाल करता है, उसे यह क्लॉक खिलाती है, और उसके नीचे ये डिवाइस लटके हैं।
प्रारूप स्वयं एक औपचारिक विनिर्देश पर टिका है (4)। इसे ट्री इसलिए कहते हैं क्योंकि यह सचमुच एक पेड़ है। यह एक रूट नोड से शुरू होता है, उससे बसें शाखाओं की तरह निकलती हैं, और उन बसों से जुड़े डिवाइस आगे शाखाएँ बनाते हैं — हार्डवेयर की भौतिक जुड़ाई का हूबहू प्रतिबिंब।
गढ़े हुए उदाहरण के बजाय एक असली उदाहरण देखिए। नीचे दिया नोड TI के J722S/AM67A परिवार के लिए U-Boot ट्री की k3-am62p-main.dtsi से है — यानी वही विवरण जिसे एक AM67A बोर्ड बूट होते समय सचमुच पढ़ता है:
main_i2c0: i2c@20000000 {
compatible = "ti,am64-i2c", "ti,omap4-i2c";
reg = <0x00 0x20000000 0x00 0x100>;
interrupts = <GIC_SPI 161 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
power-domains = <&k3_pds 102 TI_SCI_PD_EXCLUSIVE>;
clocks = <&k3_clks 102 2>;
clock-names = "fck";
status = "disabled";
};
यहाँ हर पंक्ति का मतलब है। reg मेमोरी मैप में कंट्रोलर का पता देता है — 0x20000000, लंबाई 0x100। पते संख्याओं के जोड़ों में इसलिए लिखे जाते हैं क्योंकि एड्रेस स्पेस 64-बिट है; पहली संख्या ऊपरी 32 बिट रखती है। interrupts बताता है कि कौन-सी इंटरप्ट लाइन इस्तेमाल हो रही है; GIC_SPI और IRQ_TYPE_LEVEL_HIGH जैसे नाम शामिल की गई हेडर फ़ाइलों से आते हैं — यानी डिवाइस ट्री का स्रोत सादा डेटा होते हुए भी C प्रीप्रोसेसर से गुज़रता है।
ग़ौर करने लायक़ बात यह है कि compatible में दो मान हैं: "ti,am64-i2c" और "ti,omap4-i2c"। यह सूची सबसे विशिष्ट से सबसे सामान्य की ओर पढ़ी जाती है। कर्नेल पहले am64 के लिए ख़ास ड्राइवर ढूँढ़ता है, न मिले तो पुराने और अधिक सामान्य omap4 ड्राइवर पर लौट आता है (5)। इस तरह कोई नई चिप बिना उसके लिए अलग ड्राइवर लिखे भी चल सकती है।
clocks और clock-names बताते हैं कि डिवाइस को क्लॉक कहाँ से मिलती है: &k3_clks पर डिवाइस 102 का आउटपुट 2, जिसे ड्राइवर "fck" नाम से ढूँढ़ेगा। power-domains उसी तर्क से पावर डोमेन जोड़ता है। ये आवृत्ति की सेटिंग नहीं हैं — ये जुड़ाई का विवरण हैं, कौन-सी क्लॉक कहाँ जाती है।
आख़िरी पंक्ति अहम है: status = "disabled"। चिप बनाने वाला पेरिफेरल का विवरण देता है पर उसे बंद छोड़ देता है, क्योंकि उसे नहीं पता कि वह I²C बस आपके बोर्ड पर इस्तेमाल होती भी है या नहीं। उसे चालू करना बोर्ड की अपनी .dts का काम है। J722S EVM की बोर्ड फ़ाइल में यह ऐसा दिखता है:
&main_i2c0 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&main_i2c0_pins_default>;
clock-frequency = <400000>;
exp1: gpio@23 {
compatible = "ti,tca6424";
reg = <0x23>;
gpio-controller;
#gpio-cells = <2>;
};
};
शुरू का &main_i2c0 ऊपर वाले नोड का संदर्भ है — यह नया नोड नहीं बनाता, मौजूदा के ऊपर लिखता है। status चालू होता है, पिन सौंपे जाते हैं, गति 400 kHz तय होती है, और नीचे एक असली डिवाइस जोड़ा जाता है: I²C बस पर 0x23 पते पर बैठा TCA6424 GPIO एक्सपैंडर। चूँकि कंट्रोलर ने #address-cells = <1> घोषित किया है, यह पता एक ही संख्या से लिखा जाता है — कंट्रोलर के अपने reg से अलग।
व्यवहार में डिवाइस ट्री ऐसे ही काम करता है: निर्माता SoC का विवरण एक बार देता है, और आप बताते हैं कि आपके बोर्ड पर क्या जुड़ा है।
जो गुण सचमुच मायने रखता है वह compatible है। ड्राइवर और हार्डवेयर की जोड़ी बनाने के लिए कर्नेल यहीं देखता है। ड्राइवर कहता है "मैं ti,tca6424 के अनुकूल हूँ", डिवाइस ट्री के किसी नोड में वही स्ट्रिंग होती है, और कर्नेल दोनों को मिला देता है। इसीलिए टाइपिंग की ग़लतियाँ चुपचाप विफल होती हैं: स्ट्रिंग न मिले तो कोई त्रुटि नहीं आती — डिवाइस बस कभी प्रकट ही नहीं होता।
याद रखने लायक़ बात: डिवाइस ट्री हार्डवेयर का विवरण देता है, ड्राइवर की कॉन्फ़िगरेशन का नहीं। कर्नेल की बाइंडिंग लिखने वाली मार्गदर्शिका इसी सीमा पर ख़ास ज़ोर देती है (6)। व्यवहार में यह रेखा लगातार धुँधली होती है, और कर्नेल की मेलिंग सूचियों पर सबसे ज़्यादा बहस इसी पर होती है — "क्या यह सचमुच हार्डवेयर का गुण है, या आपकी पसंद?"
फ़ाइलें
आपको तीन एक्सटेंशन दिखेंगे, और इन्हें आपस में गड्डमड्ड करना आसान है (11)।
.dts वह स्रोत है जो आप लिखते हैं, इंसान के पढ़ने लायक़ रूप। .dtsi एक टुकड़ा है जिसे दूसरी फ़ाइलें शामिल कर सकती हैं — "i" include से है। चिप निर्माता आम तौर पर पूरे SoC का विवरण एक .dtsi में देता है; आप अपने बोर्ड के लिए छोटी-सी .dts लिखते हैं, उसे शामिल करते हैं, और सिर्फ़ वही जोड़ते हैं जो आपके बोर्ड का ख़ास है।
.dtb संकलित रूप है — डिवाइस ट्री ब्लॉब। कर्नेल असल में यही पढ़ता है। कंपाइलर dtc है (7):
dtc -I dts -O dtb -o my-board.dtb my-board.dts
यह उलटी दिशा में भी चलता है, जो किसी समस्या का पीछा करते समय बेहद क़ीमती है। अगर आपके पास चलता हुआ .dtb है, तो उसे खोलकर भीतर देख सकते हैं:
dtc -I dtb -O dts -o decoded.dts my-board.dtb
चलती हुई प्रणाली पर आप डिवाइस ट्री सीधे फ़ाइल सिस्टम से भी पढ़ सकते हैं। कर्नेल जो ट्री वाक़ई इस्तेमाल कर रहा है, उसे यहाँ खोल देता है:
ls /proc/device-tree/
"जो फ़ाइल मैंने संपादित की, क्या वही लोड हुई?" — इस सवाल का यह सबसे तेज़ जवाब है। हैरान करने वाली बार-बारता से जवाब "नहीं" निकलता है।
बूट के समय क्या होता है
क्रम यह है: बूटलोडर — आम तौर पर U-Boot (8) — .dtb को मेमोरी में लोड करता है, कर्नेल लोड करता है, और उसे शुरू करते समय ब्लॉब का मेमोरी पता एक रजिस्टर में सौंप देता है। ARM पर वह रजिस्टर और बूटलोडर को जो शर्तें पूरी करनी हैं, कर्नेल के बूटिंग दस्तावेज़ में तय हैं (9)। कोई भी ड्राइवर खड़ा करने से पहले कर्नेल उस ट्री को पढ़ता है, उसमें से डिवाइस निकालता है, और compatible स्ट्रिंग के आधार पर ड्राइवर जोड़ता है।
व्यावहारिक नतीजा यह है कि कर्नेल और डिवाइस ट्री अलग-अलग अपडेट किए जा सकते हैं। नया सेंसर जोड़ने पर कर्नेल दोबारा बनाना शायद ही पड़ता है; आम तौर पर .dtb अपडेट करना काफ़ी है। पर यह दोनों तरफ़ काटता है — स्वतंत्र होने की वजह से ये आपस में बिछड़ सकते हैं। नया कर्नेल पुराने .dtb के साथ बूट कीजिए और बोर्ड चुपचाप अधूरा खड़ा होता है, और वजह समझने में वक़्त लगता है।
ओवरले का विचार भी है: चलते समय किसी मौजूदा ट्री के ऊपर लगाए जाने वाले टुकड़े (10)। रास्पबेरी पाई पर HAT लगाने पर यही होता है — मुख्य ट्री जैसा है वैसा रहता है और ऊपर एक छोटा टुकड़ा बिछ जाता है। जब बेस बोर्ड हमेशा वही हो और सिर्फ़ लगाया जाने वाला मॉड्यूल बदलता हो, तब यह काम का है।
जब कुछ काम न करे
डिवाइस ट्री की ख़राबियों में एक ख़ास तरह की झुँझलाहट है, क्योंकि वे आम तौर पर कोई त्रुटि देती ही नहीं। डिवाइस बस ग़ायब होता है। इस क्रम से चलना समय बचाता है।
पहले यह जाँचिए कि डिवाइस बना भी था या नहीं। क्या नोड /proc/device-tree के नीचे मौजूद है? नहीं है, तो समस्या उस फ़ाइल में नहीं है जिसे आपने संपादित किया — बहुत संभव है कि ग़लत .dtb लोड हो रहा हो, या नोड एक स्तर ऊपर status = "disabled" से बंद हो। यह दूसरी बात लगातार होती है: निर्माता की .dtsi फ़ाइलों में पेरिफेरल आम तौर पर निष्क्रिय आते हैं, और उम्मीद की जाती है कि आप उन्हें अपने बोर्ड की .dts में status = "okay" से एक-एक करके चालू करेंगे।
नोड मौजूद है पर कोई ड्राइवर नहीं लगा, तो नज़र compatible स्ट्रिंग पर रखिए। ड्राइवर के स्रोत में of_device_id तालिका ढूँढ़िए और स्ट्रिंग अक्षर-दर-अक्षर मिलाइए। एक अल्पविराम, एक हाइफ़न काफ़ी है।
एक बात और: dtc की चेतावनियाँ ज़्यादातर परियोजनाओं में बंद आती हैं, और वे सचमुच काम की बातें कहती हैं। पते और reg का बेमेल, ग़ायब #address-cells — ठीक उसी क़िस्म की ख़राबी जो बूट पर ख़ामोश नाकामी बन जाती है। बिल्ड करते समय चेतावनियाँ चालू रखना फ़ायदे का सौदा है।
अंत में
डिवाइस ट्री को समझना इसलिए मुश्किल लगता है क्योंकि शुरू में कोई नहीं बताता कि यह असल में है क्या। यह कॉन्फ़िगरेशन फ़ाइल जैसा दिखता है, पर है नहीं; ड्राइवर जैसा दिखता है, पर वह भी नहीं। सबसे उपयोगी ढाँचा यह है कि इसे हार्डवेयर और सॉफ़्टवेयर के बीच का अनुबंध मानिए: बोर्ड कैसे जुड़ा है, उस भाषा में लिखा हुआ जिसे कर्नेल समझता है।
एक बार यह उस ढाँचे में बैठ जाए, तो बाक़ी अपने आप चला आता है। compatible इतना निर्णायक क्यों है, .dtb कर्नेल से अलग क्यों रहता है, कुछ ग़ायब हो तो सबसे पहले /proc/device-tree क्यों देखना चाहिए — सब कुछ उसी एक विचार से निकलता है।
संबंधित लेख
- Embedded Linux बूट चेन — बूटलोडर कर्नेल को नियंत्रण कैसे सौंपता है
- DFU क्या है? — जब और कोई रास्ता न बचे तो USB से फ़र्मवेयर लिखना
संदर्भ
- Platform Devices and Drivers — SoC पेरिफेरल PCI और USB की तरह गणनीय क्यों नहीं, और बोर्ड-विशिष्ट कोड उन्हें कैसे पंजीकृत करता है।
- How To Write Linux PCI Drivers —
pci_device_idतालिका और मेल खाने वाले डिवाइसों के लिएprobe()कॉल। - USB Device Drivers — USB की तरफ़ आईडी आधारित ड्राइवर मिलान।
- Devicetree Specification — प्रारूप की औपचारिक परिभाषा।
- Linux and the Devicetree — कर्नेल ट्री कैसे पढ़ता है और ड्राइवर कैसे जोड़ता है।
- Writing Devicetree Bindings — हार्डवेयर के विवरण और ड्राइवर की कॉन्फ़िगरेशन के बीच की सीमा।
- dtc — Device Tree Compiler — कंपाइलर का अपना रिपॉज़िटरी और उपयोग।
- U-Boot: Devicetree Control — U-Boot डिवाइस ट्री को कैसे संभालता है।
- Booting ARM Linux — बूटलोडर किस रजिस्टर से ब्लॉब कर्नेल को सौंपता है।
- Devicetree Overlay Notes — ओवरले चलते समय कैसे लागू होते हैं।
- Device Tree Reference (eLinux) — उदाहरणों सहित सामुदायिक संदर्भ।
टिप्पणियाँ