24 पॉइंट द्वारा GN⁺ 2026-04-10 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • 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 में lsusb command से डिवाइस की जानकारी देखी जा सकती है
    • उदाहरण: ID 18d1:4ee0 Google Inc. Nexus/Pixel Device (fastboot)
    • 18d1 Google का VID है, और 4ee0 Nexus/Pixel bootloader का PID है
  • lsusb -t command से 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:PID combination वाला डिवाइस connect होने पर callback चले
  • प्रोग्राम चलने के बाद डिवाइस connect करते ही "Device plugged in!" message प्रिंट होता है
  • Linux में यह डिफ़ॉल्ट रूप से काम करता है, और ज़रूरत पड़ने पर libusb_detach_kernel_driver() से kernel driver को अलग किया जा सकता है
  • Windows में Winusb.sys driver की ज़रूरत होती है, और अगर वह न हो तो 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 को
  • इसके बाद GET_DESCRIPTOR request से device descriptor लाया जा सकता है
    • लौटे हुए data में idVendor, idProduct, bDeviceClass जैसी डिवाइस जानकारी शामिल होती है
  • lsusb -v command से सभी descriptors (device, configuration, interface, endpoint आदि) विस्तार से देखे जा सकते हैं
    • उदाहरण: Android Fastboot interface में Bulk IN(0x81) और Bulk OUT(0x02) endpoints मौजूद हैं

एंडपॉइंट

  • endpoint network port जैसी अवधारणा है, जिसके ज़रिए डिवाइस data send/receive करता है
  • हर endpoint का type और direction descriptor में परिभाषित होता है
  • Control transfer type

    • हर डिवाइस में एक होता है और उसका address हमेशा 0x00 होता है
    • शुरुआती setup और डिवाइस जानकारी माँगने के लिए उपयोग होता है
    • यह interface का हिस्सा नहीं होता, बल्कि खुद डिवाइस का हिस्सा होता है
  • 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
      
    • OKAY success status है, और 0.4 Fastboot version है

समापन

  • kernel code लिखे बिना user space में पूरा USB ड्राइवर लागू करना संभव है
  • सभी USB drivers एक ही बुनियादी सिद्धांत का पालन करते हैं, अंतर सिर्फ protocol का होता है
  • जटिल protocols (MTP आदि) की भी बुनियादी संरचना यही रहती है, इसलिए इन्हें socket communication जैसी अवधारणा के साथ समझा जा सकता है

1 टिप्पणियां

 
GN⁺ 2026-04-10
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 भी जोड़ दे, तो काफ़ी उपयोगी हो सकता है

    • Guitar Center की waiting period सिर्फ यह जाँचने के लिए नहीं होती कि सामान चोरी का है या नहीं। कानूनन इसे pawn shop की तरह कुछ समय तक बेचने की मनाही होती है, ताकि मूल मालिक के पास उसे वापस लेने का समय रहे
    • मैं भी यही डिवाइस इस्तेमाल करता हूँ और उस driver को AUR में package किया था। binary blob काम नहीं करता था, लेकिन साधारण MIDI router के रूप में यह काफ़ी है
  • अगर आप Go language में ऐसा कुछ करना चाहते हैं, तो मैंने go-usb library बनाई है, जो cgo के बिना USB access देती है
    इसी से मैंने UVC device संभालने के लिए go-uvc भी बनाया

    • Rust में मैं nusb recommend करूँगा
  • मैं भी हाल ही में Macbook M3 पर usbip system को कुछ इसी तरह implement कर रहा हूँ
    लेकिन नए macOS में restrictions हैं। जिन USB devices को system खुद पहचान लेता है, उनके लिए libusb-आधारित user-space driver नहीं बनाया जा सकता, जब तक कि security features को manually disable न किया जाए

    • driver override सिर्फ एक layer पर adjust करना पड़ता है, इसलिए इसे कुछ हद तक कम किया जा सकता है
  • इस approach में आख़िरकार USB driver ही application code की भूमिका भी निभा रहा होता है। यानी यह driver से ज़्यादा library+program जैसा है
    उदाहरण के लिए, अगर किसी USB-Ethernet device को OS के network adapter की तरह जोड़ना हो, तो यह कैसे किया जाएगा, यह जानने की जिज्ञासा है

    • standardized devices आम तौर पर USB/CDC/ECM या RNDIS इस्तेमाल करती हैं, इसलिए auto-detect हो जाती हैं। user-space access उल्टे non-standard devices के लिए ज़्यादा उपयोगी है। Windows में इसे driver signing के बिना libusb से portable तरीके से implement किया जा सकता है
    • Linux में tun/tap device बनाकर user space से kernel के साथ communicate किया जा सकता है, या दूसरे subsystems भी user space में चलाने पड़ते हैं
  • अगर मैंने यह लेख कुछ साल पहले पढ़ा होता, तो 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 सीधे टाइप किया जा सके

    • वह programming font ligature है। copy करने पर वह वास्तव में -> ही निकलता है। यह modern C++ का trailing return type syntax है
    • कुछ developers ligature fonts पसंद करते हैं। वे दो characters को एक glyph में जोड़ देती हैं
    • अगर Compose key सेट कर लें, तो किसी भी keyboard से “→” टाइप किया जा सकता है
    • आख़िरकार वह सिर्फ "->" ही है। font उसे arrow की तरह render कर रहा है
  • मुझे जिज्ञासा थी कि USB devices DMA support करती हैं या नहीं। क्या सब कुछ host के ज़रिए ही होता है, या device सीधे memory access भी कर सकती है

    • USB devices, PCIe या FireWire की तरह host memory को सीधे access नहीं करतीं। इसके बजाय XHCI controller DMA करता है, और ज़्यादातर device controllers अपनी RAM और USB के बीच DMA support करते हैं
    • सभी transfers host-driven होते हैं। device ऐसा लग सकता है कि वह पहले data भेज रही है, लेकिन वास्तव में host ही request करता है। direct DMA सुरक्षा के लिहाज़ से बड़ा जोखिम होता
  • मैंने पहले एक simple USB device बनाने की कोशिश की थी, लेकिन descriptor लिखने के तरीके पर लगभग कोई जानकारी नहीं थी। ज़्यादातर सलाह यही थी कि “मिलती-जुलती device ढूँढो, copy करो, फिर modify करो।” तब लगा कि क्या USB सच में इतना शानदार standard है

    • descriptors मुझे भी रहस्यमय लगते थे, लेकिन बाद में समझ आया कि वे बस fixed binary structs हैं। हर USB class जिन fields और endpoints को define करती है, उन्हें सही रखो, तो device पहचान ली जाती है
    • USB ठीक-ठाक है, लेकिन electrical perspective से USB 1/2 सच्चा differential signaling नहीं है
    • tutorial material लगभग नहीं के बराबर है, लेकिन किसी बड़े enterprise standard के हिसाब से यह काफ़ी reasonable है। हाँ, choices बहुत ज़्यादा हैं, इसलिए कई specs पढ़नी पड़ती हैं
  • अगर कोई मुझसे कहे कि “USB device driver खुद लिखो”, तो मैं पहले device वापस कर दूँगा और देखूँगा कि क्या इसे virtual COM port की तरह संभाला जा सकता है