- USB ड्राइवर डेवलपमेंट को अक्सर kernel-level काम माना जाता है, लेकिन वास्तव में इसे socket programming जैसी कठिनाई के साथ user space में भी लागू किया जा सकता है
- libusb का उपयोग करके kernel code लिखे बिना device enumeration, control transfer, और data send/receive सब किया जा सकता है
- USB communication Control, Bulk, Interrupt, Isochronous इन चार transfer types और IN/OUT directions से मिलकर बना होता है, और हर endpoint एक one-way channel की तरह काम करता है
- Android डिवाइस के Fastboot protocol को उदाहरण के रूप में लेकर, Bulk endpoint के जरिए command और response का आदान-प्रदान code से दिखाया गया है
- user space में भी पूरा USB ड्राइवर लागू किया जा सकता है, और सभी USB protocols एक जैसी बुनियादी संरचना साझा करते हैं
परिचय
- USB डिवाइस के लिए ड्राइवर लिखना अक्सर इसलिए कठिन लगता है क्योंकि यह माना जाता है कि इसके लिए kernel code संभालना पड़ता है, लेकिन वास्तव में इसकी जटिलता socket इस्तेमाल करने वाले application-level code जैसी है
- जिन डेवलपर्स के पास hardware का ज़्यादा अनुभव नहीं है, वे भी user space में USB को handle करने का तरीका सीख सकते हैं
- USB के detailed internals पर सामग्री उपलब्ध है, लेकिन शुरुआती लोगों के लिए उस तक पहुँचना कठिन हो सकता है
- USB इस्तेमाल करने के लिए embedded systems स्तर की जानकारी ज़रूरी नहीं है, इसे network socket की तरह समझकर अपनाया जा सकता है
USB डिवाइस
- उदाहरण के लिए bootloader mode में Android smartphone का उपयोग किया गया है
- यह आसानी से उपलब्ध है, protocol सरल है, और OS में default driver न होने की वजह से प्रयोग के लिए उपयुक्त है
- bootloader mode में प्रवेश हर डिवाइस में अलग हो सकता है, लेकिन आम तौर पर power button और volume button के combination से किया जा सकता है
डिवाइस की मैन्युअल enumeration
- Enumeration वह प्रक्रिया है जिसमें host डिवाइस की जानकारी माँगकर उसकी पहचान करता है, और यह डिवाइस connect होने पर अपने आप होती है
- standard devices में USB class के आधार पर driver अपने आप load हो जाता है, जबकि vendor-specific devices के लिए
VID(Vendor ID) औरPID(Product ID) का उपयोग होता है - Linux में
lsusbcommand से डिवाइस की जानकारी देखी जा सकती है- उदाहरण:
ID 18d1:4ee0 Google Inc. Nexus/Pixel Device (fastboot) 18d1Google का VID है, और4ee0Nexus/Pixel bootloader का PID है
- उदाहरण:
lsusb -tcommand से class और driver status देखा जा सकता हैClass=Vendor Specific Class,Driver=[none]दिखने का मतलब है कि OS ने कोई driver load नहीं किया है
- Windows में यही जानकारी Device Manager या USB Device Tree Viewer से देखी जा सकती है
libusb से डिवाइस enumeration
- libusb library का उपयोग करके kernel code लिखे बिना user space में USB डिवाइस से communication किया जा सकता है
libusb_hotplug_register_callback()से इस तरह सेट किया जा सकता है कि किसी खासVID:PIDcombination वाला डिवाइस connect होने पर callback चले- प्रोग्राम चलने के बाद डिवाइस connect करते ही
"Device plugged in!"message प्रिंट होता है - Linux में यह डिफ़ॉल्ट रूप से काम करता है, और ज़रूरत पड़ने पर
libusb_detach_kernel_driver()से kernel driver को अलग किया जा सकता है - Windows में
Winusb.sysdriver की ज़रूरत होती है, और अगर वह न हो तो Zadig tool से उसे manually replace किया जा सकता है
डिवाइस से communication
- USB डिवाइस के साथ पहला communication Control endpoint (address 0x00) के ज़रिए होता है
libusb_control_transfer()से standard request (GET_STATUS) भेजकर डिवाइस की स्थिति पढ़ी जा सकती है- उदाहरण response:
01 00→ पहला byte Self-Powered को दिखाता है, दूसरा Remote Wakeup unsupported को
- उदाहरण response:
- इसके बाद GET_DESCRIPTOR request से device descriptor लाया जा सकता है
- लौटे हुए data में
idVendor,idProduct,bDeviceClassजैसी डिवाइस जानकारी शामिल होती है
- लौटे हुए data में
lsusb -vcommand से सभी descriptors (device, configuration, interface, endpoint आदि) विस्तार से देखे जा सकते हैं- उदाहरण:
Android Fastbootinterface में Bulk IN(0x81) और Bulk OUT(0x02) endpoints मौजूद हैं
- उदाहरण:
एंडपॉइंट
- endpoint network port जैसी अवधारणा है, जिसके ज़रिए डिवाइस data send/receive करता है
- हर endpoint का type और direction descriptor में परिभाषित होता है
-
Control transfer type
- हर डिवाइस में एक होता है और उसका address हमेशा
0x00होता है - शुरुआती setup और डिवाइस जानकारी माँगने के लिए उपयोग होता है
- यह interface का हिस्सा नहीं होता, बल्कि खुद डिवाइस का हिस्सा होता है
- हर डिवाइस में एक होता है और उसका address हमेशा
-
Bulk transfer type
- बड़ी मात्रा में non-real-time data transfer के लिए उपयोग होता है
- उदाहरण: Mass Storage, CDC-ACM (serial), RNDIS (Ethernet)
- bandwidth अधिक होती है, लेकिन priority कम होती है
-
Interrupt transfer type
- कम मात्रा में low-latency data transfer के लिए उपयोग होता है
- keyboard, mouse जैसे डिवाइस में button input को तेज़ी से poll करने के लिए उपयोग होता है
- यह असली hardware interrupt नहीं होता, host इसे periodic request के रूप में करता है
-
Isochronous transfer type
- time-sensitive high-volume data (audio, video streaming) के लिए उपयोग होता है
- अगर delay हो जाए तो quality पर तुरंत असर दिखता है
- libusb में इसे asynchronous तरीके से handle किया जाता है
-
IN / OUT direction
- USB host-centric architecture पर आधारित है, इसलिए डिवाइस request मिलने से पहले data नहीं भेजता
IN: वह दिशा जिसमें host data प्राप्त करता हैOUT: वह दिशा जिसमें host data भेजता है- अगर endpoint address का सबसे ऊपरी bit (MSB)
1है, तो वह IN है;0है, तो OUT - अधिकतम 127 user-defined endpoints उपयोग किए जा सकते हैं (
0x00सिर्फ Control के लिए आरक्षित है) - endpoint one-way होते हैं, और Fastboot interface की तरह IN/OUT pairs में बने होते हैं
Fastboot protocol
- Fastboot Android bootloader communication protocol है, जिसमें command string भेजी जाती है और बदले में 4-byte status code तथा data मिलता है
- उदाहरण:
Host: "getvar:version"→Client: "OKAY0.4"Host: "getvar:nonexistant"→Client: "OKAY"
- उदाहरण:
- libusb का उपयोग करके Fastboot command भेजने का code example
libusb_claim_interface()से interface 0 को claim किया जाता है"getvar:version"command को Bulk OUT(0x02) endpoint पर भेजा जाता है- Bulk IN(0x81) endpoint से response प्राप्त किया जाता है
- output उदाहरण:
Request: getvar:version Response: OKAY0.4 OKAYsuccess status है, और0.4Fastboot version है
समापन
- kernel code लिखे बिना user space में पूरा USB ड्राइवर लागू करना संभव है
- सभी USB drivers एक ही बुनियादी सिद्धांत का पालन करते हैं, अंतर सिर्फ protocol का होता है
- जटिल protocols (MTP आदि) की भी बुनियादी संरचना यही रहती है, इसलिए इन्हें socket communication जैसी अवधारणा के साथ समझा जा सकता है
1 टिप्पणियां
Hacker News की राय
टाइमिंग बिल्कुल परफेक्ट रही। मैं जल्द ही स्थानीय Guitar Center से MOTU MIDI Express XT लेने वाला हूँ
यह सेकंड-हैंड उपकरण है, इसलिए कानूनी तौर पर इसे कुछ समय तक होल्ड पर रखना पड़ता है और मैं उसी का इंतज़ार कर रहा हूँ। समस्या यह है कि यह डिवाइस standard MIDI-over-USB नहीं बल्कि एक proprietary protocol इस्तेमाल करती है, इसलिए Linux, OpenBSD, Haiku जैसी मेरी systems पर इसे USB से सीधे इस्तेमाल नहीं किया जा सकता
अभी के लिए मुझे सिर्फ synth module और controller के बीच routing चाहिए, इसलिए काम चल जाएगा, लेकिन अच्छा होगा अगर इसे PC side पर भी चलाया जा सके
मौजूदा Linux driver है, लेकिन उसकी stability भी पक्की नहीं लगती और XT support है या नहीं, यह भी साफ नहीं है। kernel panic की समस्या ठीक हो चुकी बताई जाती है, लेकिन issues अभी भी बाकी हैं
इसलिए मैं खुद LibUSB-आधारित user-space driver बनाने की सोच रहा हूँ। अगर यह MIDI ports expose करे और routing tooling भी जोड़ दे, तो काफ़ी उपयोगी हो सकता है
अगर आप Go language में ऐसा कुछ करना चाहते हैं, तो मैंने go-usb library बनाई है, जो cgo के बिना USB access देती है
इसी से मैंने UVC device संभालने के लिए go-uvc भी बनाया
मैं भी हाल ही में Macbook M3 पर usbip system को कुछ इसी तरह implement कर रहा हूँ
लेकिन नए macOS में restrictions हैं। जिन USB devices को system खुद पहचान लेता है, उनके लिए libusb-आधारित user-space driver नहीं बनाया जा सकता, जब तक कि security features को manually disable न किया जाए
इस approach में आख़िरकार USB driver ही application code की भूमिका भी निभा रहा होता है। यानी यह driver से ज़्यादा library+program जैसा है
उदाहरण के लिए, अगर किसी USB-Ethernet device को OS के network adapter की तरह जोड़ना हो, तो यह कैसे किया जाएगा, यह जानने की जिज्ञासा है
अगर मैंने यह लेख कुछ साल पहले पढ़ा होता, तो laptop features को reverse engineer करना काफ़ी आसान हो जाता। खासकर keyboard LED control program आज भी मेरे पसंदीदा projects में से एक है
यह सचमुच बहुत काम का introductory guide था। low-level hardware API के साथ काम करना मुश्किल है, लेकिन rewarding भी है। modern OS की abstraction layers ने बहुत कुछ आसान बना दिया है, फिर भी उनके नीचे क्या है यह समझना अब भी महत्वपूर्ण है
C++ code थोड़ा अजीब लगा। मैंने ऐसा keyboard कभी नहीं देखा जिसमें arrow character सीधे टाइप किया जा सके
->ही निकलता है। यह modern C++ का trailing return type syntax है"->"ही है। font उसे arrow की तरह render कर रहा हैमुझे जिज्ञासा थी कि USB devices DMA support करती हैं या नहीं। क्या सब कुछ host के ज़रिए ही होता है, या device सीधे memory access भी कर सकती है
मैंने पहले एक simple USB device बनाने की कोशिश की थी, लेकिन descriptor लिखने के तरीके पर लगभग कोई जानकारी नहीं थी। ज़्यादातर सलाह यही थी कि “मिलती-जुलती device ढूँढो, copy करो, फिर modify करो।” तब लगा कि क्या USB सच में इतना शानदार standard है
अगर कोई मुझसे कहे कि “USB device driver खुद लिखो”, तो मैं पहले device वापस कर दूँगा और देखूँगा कि क्या इसे virtual COM port की तरह संभाला जा सकता है