UHCI, या बहुत पहले USB



शुभ दिन, प्रिय पाठक! मुझे UHCI के बारे में लिखने के लिए कहा गया - ठीक है, मैं लिखता हूं।

आपको यह लेख उपयोगी लग सकता है, उदाहरण के लिए, आपके पास ड्राइवरों के लिए पर्याप्त लेखन कौशल और हार्डवेयर के लिए दस्तावेज़ीकरण पढ़ना नहीं है। एक सरल उदाहरण: आप अपने ओएस को एक मिनी-पीसी के लिए लिखना चाहते हैं, ताकि कुछ विंडोज या अन्य लिनक्स वितरण लोहा डाउनलोड न करें, और आप इसकी सभी शक्ति का उपयोग विशेष रूप से अपने स्वयं के प्रयोजनों के लिए करते हैं।

UHCI क्या है?


मुझे लगता है, किस और क्यों के विषय पर फिर से स्प्रे न करने के लिए, बस EHCI के बारे में मेरे पिछले लेख का लिंक छोड़ दें। यहां प्रहार करो
UHCI - यूनिवर्सल होस्ट कंट्रोलर इंटरफ़ेस, PCI-device के रूप में काम करता है, लेकिन, EHCI के विपरीत MMIO (मेमोरी-मैप्ड-IO) के बजाय पोर्ट का उपयोग करता है।



इसके बाद उपयोग किए जाने वाले शब्द


  • USB ड्राइवर (USBD) - USB ड्राइवर ही
  • HC (होस्ट कंट्रोलर) - एक होस्ट कंट्रोलर, या सिर्फ हमारा UHCI
  • होस्ट कंट्रोलर ड्राइवर (HCD) - एक ड्राइवर जो हार्डवेयर और USBD को जोड़ता है
  • USB डिवाइस - USB डिवाइस ही

डेटा ट्रांसफर के प्रकार


Isochronous - isosynchronous ट्रांसमिशन, जिसमें डेटा ट्रांसफर की आवृत्ति दी गई है। इसका उपयोग, उदाहरण के लिए, यूएसबी माइक्रोफोन आदि के लिए किया जा सकता है।

रुकावट - एक उपकरण से छोटे, सहज डेटा स्थानान्तरण। इंटरेक्शन ट्रांसमिशन प्रकार उन उपकरणों का समर्थन करता है जिनके लिए एक पूर्वानुमानित सेवा अंतराल की आवश्यकता होती है, लेकिन जरूरी नहीं कि एक पूर्वानुमान योग्य डेटा स्ट्रीम प्रदान करें। आमतौर पर कीबोर्ड और पॉइंटिंग डिवाइस जैसे उपकरणों के लिए उपयोग किया जाता है जो संभवत: विस्तारित अवधि के लिए डेटा प्रदान नहीं करते हैं, लेकिन जब उन्हें भेजने के लिए डेटा की त्वरित प्रतिक्रिया की आवश्यकता होती है।

नियंत्रण - डिवाइस की स्थिति, स्थिति और कॉन्फ़िगरेशन के बारे में जानकारी के प्रसारण का प्रकार। कंट्रोल ट्रांसफर प्रकार का उपयोग होस्ट से यूएसबी डिवाइसों पर नियंत्रण चैनल प्रदान करने के लिए किया जाता है। नियंत्रण प्रसारण में हमेशा सेटअप चरण और स्थिति चरण के बाद शून्य या अधिक डेटा चरण होते हैं। यह जरूरी है कि किसी दिए गए समापन बिंदु पर नियंत्रण हस्तांतरण को फीफो मोड में संसाधित किया जाए। यदि नियंत्रण एक ही समापन बिंदु के लिए पारित किया जाता है, तो इंटरलेविंग से अप्रत्याशित व्यवहार हो सकता है।

बल्क - डेटा सरणियों के हस्तांतरण का प्रकार। उदाहरण के लिए, MassStorage उपकरणों में उपयोग किया जाता है।



यह 1ms समय वितरण कैसा दिखता है - एक फ्रेम का प्रसंस्करण।

समय वितरण


होस्ट कंट्रोलर हर 1 ms के स्टार्ट ऑफ़ फ्रेम (SOF) पैकेट को जेनरेट करके रियल-टाइम डेटा डिलीवरी का समर्थन करता है। एक SOF पैकेट तब उत्पन्न होता है जब मेजबान नियंत्रक में SOF काउंटर समाप्त हो जाता है (चित्र 3)। मेजबान नियंत्रक 1 एमएस के एक फ्रेम समय के लिए SOF काउंटर को इनिशियलाइज़ करता है। SOF परिवर्तन रजिस्टर को प्रोग्रामिंग करके इस मान (और इसलिए फ्रेम समय अवधि) में छोटे बदलाव किए जा सकते हैं। यह सुविधा आपको संपूर्ण USB सिस्टम में रीयल-टाइम सिंक्रनाइज़ेशन बनाए रखने के लिए, यदि आवश्यक हो, तो फ़्रेम समय अवधि में मामूली बदलाव करने की अनुमति देती है।

होस्ट नियंत्रक में प्रत्येक SOF पैकेट में फ़्रेम संख्या शामिल होती है। यह फ़्रेम संख्या विशिष्ट रूप से वास्तविक समय में फ़्रेम की अवधि निर्धारित करती है। फ़्रेम स्थिति का अंत (ईओएफ) 1 एमएस समय अंतराल के अंत में होता है जब मेजबान नियंत्रक अगले फ्रेम समय शुरू करता है, इसी फ्रेम संख्या के साथ एक और एसओएफ पैकेट उत्पन्न करता है। फ़्रेम अवधि के दौरान, डेटा को सूचना पैकेट के रूप में प्रेषित किया जाता है। फ्रेम समय अवधि सख्ती से मेजबान नियंत्रक द्वारा लागू की जाती है, और वर्तमान फ्रेम में डेटा पैकेट ईओएफ से परे नहीं जा सकते हैं (यूएसबी विनिर्देश में अध्याय 11 देखें)। होस्ट कंट्रोलर वास्तविक समय में फ़्रेम के बीच डेटा ट्रांसमिशन के सिंक्रनाइज़ेशन का समर्थन करता है, फ़्रेम सूची में एक विशिष्ट प्रविष्टि करने के लिए फ़्रेम नंबर को जोड़ता है। होस्ट कंट्रोलर का फ़्रेम काउंटर एक फ़्रेम नंबर (11-बिट मान) उत्पन्न करता है और इसे प्रत्येक SOF पैकेट में शामिल करता है। काउंटर को रजिस्टरों के माध्यम से प्रोग्राम किया जाता है और प्रत्येक फ्रेम की अवधि बढ़ाई जाती है। होस्ट नियंत्रक 1024 फ्रेम के साथ फ्रेम सूची में एक सूचकांक के रूप में फ्रेम संख्या के निचले 10 बिट्स का उपयोग करता है, जो सिस्टम मेमोरी में संग्रहीत होता है। इस प्रकार, चूंकि फ्रेम काउंटर फ़्रेम की सूची से एक प्रविष्टि के चयन को नियंत्रित करता है, होस्ट नियंत्रक प्रत्येक दिए गए फ़्रेम अवधि में सूची में प्रत्येक प्रविष्टि की प्रक्रिया करता है। होस्ट नियंत्रक प्रत्येक नए फ़्रेम के लिए फ़्रेम सूची में अगली प्रविष्टि का विस्तार करता है। यह सुनिश्चित करता है कि समकालिक प्रसारण एक विशिष्ट फ्रेम में किया जाता है।

चित्र 3:



UHCI संरचना


ईएचसीआई के साथ सब कुछ ठीक वैसा ही है। उदाहरण के लिए अनुरोध HC:



UHCI को कॉन्फ़िगर और एक्सेस करें


और इसलिए, जैसा कि मैंने पहले कहा था, यूएचसीआई बंदरगाहों के माध्यम से काम करता है, इसलिए पीसीआई से हमें यूएचसीआई रजिस्टरों के आधार का पता लगाने की आवश्यकता है।



ऑफसेट 0x20 में 4 बाइट्स हैं - IO बेस। IO बेस के बारे में, हम निम्नलिखित रजिस्टरों का उपयोग कर सकते हैं:



UHCI रजिस्टर


  • USBCMD HC को नियंत्रित करने के लिए एक रजिस्टर है। बिट्स:
    • बिट 6 एक झंडा है जिसे डिवाइस को कॉन्फ़िगर किया गया है और सफलतापूर्वक प्रारंभ किया गया है।
    • बिट 1 - एचसी रीसेट। HC को रीसेट करने के लिए सेट करें।
    • बिट 0 - रन / स्टॉप। HC की स्थिति प्रदर्शित करता है। 1 - काम करता है, 0 - नहीं।
  • USBSTS - स्थिति रजिस्टर। बिट्स:
    • बिट 5 - एचसी हाल्टेड। कोई त्रुटि उत्पन्न हुई है, या नियंत्रक ने HC रीसेट को सफलतापूर्वक पूरा कर लिया है।
    • बिट 4 - मेजबान नियंत्रक प्रक्रिया त्रुटि। जब कोई महत्वपूर्ण त्रुटि हुई है, तो बिट 1 पर सेट है और HC कतार और TD को जारी नहीं रख सकता है।
    • बिट 3 - होस्ट सिस्टम त्रुटि। PCI त्रुटि।
    • बिट 1 - एरर इंटरप्ट। इंगित करता है कि एक त्रुटि हुई है और HC ने एक बाधा उत्पन्न की है।
    • बिट ० - व्यवधान। इंगित करता है कि HC ने एक बाधा उत्पन्न की।
  • USBINTR - इंटरप्ट सेटिंग्स का रजिस्टर। बिट्स:
    • बिट 2 - आईओसी - पूर्ण पर रुकावट - लेनदेन के अंत में एक बाधा उत्पन्न करता है।
  • FRNUM - वर्तमान फ्रेम की संख्या (इसे ले लो और 0x3FF सही मान के लिए)।
  • FLBASEADD - फ़्रेम सूची आधार पता - फ़्रेम की सूची का पता।
  • PORCS - पोर्ट स्टेटस और कंट्रोल - स्टेटस और पोर्ट कंट्रोल रजिस्टर। बिट्स:
    • बिट 9 - पोर्ट रीसेट - 1 - पोर्ट को रीसेट करने के लिए।
    • बिट 8 - इंगित करता है कि एक कम गति वाला डिवाइस पोर्ट से जुड़ा है
    • बिट 3 - इंगित करता है कि राज्य पर बंदरगाह बदल गया है
    • बिट 2 - इंगित करता है कि पोर्ट सक्षम है
    • बिट 1 - इंगित करता है कि डिवाइस की स्थिति पोर्ट से जुड़ी है
    • बिट 0 - इंगित करता है कि डिवाइस पोर्ट से जुड़ा है।

संरचना


फ़्रेम सूची सूचक




स्थानांतरण descrptor




टीडी नियंत्रण और स्थिति
। बिट्स:
  • बिट्स 28-27 - त्रुटि काउंटर, ईएचसीआई के समान।
    • बिट 26 - 1 = लो-स्पीड डिवाइस, 0 = फुल-स्पीड डिवाइस।
    • बिट 25 - 1 = आइसोसिंक्रोनस टीडी
    • बिट 24 - आईओसी
    • बिट्स 23-16 - स्थिति:
    • बिट 23 - इंगित करता है कि यह एक सक्रिय टीडी है
    • बिट 22 - रुका हुआ
    • बिट 21 - डेटा बफर त्रुटि
    • बिट 20 - बब्बल का पता लगाया
    • बिट 19 - एनएके
  • बिट्स 10–0: मेजबान नियंत्रक द्वारा प्रेषित बाइट्स की संख्या।

टीडी टोकन

  • बिट्स 31:21 - मैक्स पैकेट लेन, ईएचसीआई के समान
  • बिट 19 - डेटा टॉगल, ईएचसीआई के समान
  • बिट्स 18:15 - समापन बिंदु संख्या
  • बिट्स 18:14 - डिवाइस पता
  • बिट्स 7: 0 - पीआईडी। = 0x69 में, = 0xE1, सेटअप = 0x2D

कतार सिर




कोड


प्रारंभ करें और HC को कॉन्फ़िगर करें:

PciBar bar; PciGetBar(&bar, id, 4); if (~bar.flags & PCI_BAR_IO) { // Only Port I/O supported return; } unsigned int ioAddr = bar.u.port; UhciController *hc = VMAlloc(sizeof(UhciController)); hc->ioAddr = ioAddr; hc->frameList = VMAlloc(1024 * sizeof(u32) + 8292); hc->frameList = ((int)hc->frameList / 4096) * 4096 + 4096; hc->qhPool = (UhciQH *)VMAlloc(sizeof(UhciQH) * MAX_QH + 8292); hc->qhPool = ((int)hc->qhPool / 4096) * 4096 + 4096; hc->tdPool = (UhciTD *)VMAlloc(sizeof(UhciTD) * MAX_TD + 8292); hc->tdPool = ((int)hc->tdPool / 4096) * 4096 + 4096; memset(hc->qhPool, 0, sizeof(UhciQH) * MAX_QH); memset(hc->tdPool, 0, sizeof(UhciTD) * MAX_TD); memset(hc->frameList, 0, 4 * 1024); // Frame list setup UhciQH *qh = UhciAllocQH(hc); qh->head = TD_PTR_TERMINATE; qh->element = TD_PTR_TERMINATE; qh->transfer = 0; qh->qhLink.prev = &qh->qhLink; qh->qhLink.next = &qh->qhLink; hc->asyncQH = qh; for (uint i = 0; i < 1024; ++i) hc->frameList[i] = 2 | (u32)(uintptr_t)qh; IoWrite16(hc->ioAddr + REG_INTR, 0); IoWrite16(hc->ioAddr + REG_CMD, IoRead16(hc->ioAddr + REG_CMD)&(~1)); unsigned short cfg = PciRead16(id, 4); PciWrite16(id, 4, cfg & (~1)); PciWrite16(id, 0x20, (short)-1); unsigned short size = ~(PciRead16(id, 0x20)&(~3)) + 1; PciWrite16(id, 0x20, hc->ioAddr); PciWrite16(id, 4, cfg | 5); // Disable Legacy Support IoWrite16(hc->ioAddr + REG_LEGSUP, 0x8f00); // Disable interrupts IoWrite16(hc->ioAddr + REG_INTR, 0); // Assign frame list IoWrite16(hc->ioAddr + REG_FRNUM, 0); IoWrite32(hc->ioAddr + REG_FRBASEADD, (int)hc->frameList); IoWrite16(hc->ioAddr + REG_SOFMOD, 0x40); // Clear status IoWrite16(hc->ioAddr + REG_STS, 0xffff); // Enable controller IoWrite16(hc->ioAddr + REG_CMD, 0x1); // Probe devices UhciProbe(hc, size); 

समापन बिंदु और नियंत्रण अनुरोध:

 // ------------------------------------------------------------------------------------------------ static void UhciDevControl(UsbDevice *dev, UsbTransfer *t) { UhciController *hc = (UhciController *)dev->hc; UsbDevReq *req = t->req; // Determine transfer properties uint speed = dev->speed; uint addr = dev->addr; uint endp = 0; uint maxSize = dev->maxPacketSize; uint type = req->type; uint len = req->len; // Create queue of transfer descriptors UhciTD *td = UhciAllocTD(hc); if (!td) { return; } UhciTD *head = td; UhciTD *prev = 0; // Setup packet uint toggle = 0; uint packetType = TD_PACKET_SETUP; uint packetSize = sizeof(UsbDevReq); UhciInitTD(td, prev, speed, addr, endp, toggle, packetType, packetSize, req); prev = td; // Data in/out packets packetType = type & RT_DEV_TO_HOST ? TD_PACKET_IN : TD_PACKET_OUT; u8 *it = (u8 *)t->data; u8 *end = it + len; while (it < end) { td = UhciAllocTD(hc); if (!td) { return; } toggle ^= 1; packetSize = end - it; if (packetSize > maxSize) { packetSize = maxSize; } UhciInitTD(td, prev, speed, addr, endp, toggle, packetType, packetSize, it); it += packetSize; prev = td; } // Status packet td = UhciAllocTD(hc); if (!td) { return; } toggle = 1; packetType = type & RT_DEV_TO_HOST ? TD_PACKET_OUT : TD_PACKET_IN; UhciInitTD(td, prev, speed, addr, endp, toggle, packetType, 0, 0); // Initialize queue head UhciQH *qh = UhciAllocQH(hc); UhciInitQH(qh, t, head); // Wait until queue has been processed UhciInsertQH(hc, qh); UhciWaitForQH(hc, qh); } // ------------------------------------------------------------------------------------------------ static void UhciDevIntr(UsbDevice *dev, UsbTransfer *t) { UhciController *hc = (UhciController *)dev->hc; // Determine transfer properties uint speed = dev->speed; uint addr = dev->addr; uint endp = t->endp->desc->addr & 0xf; // Create queue of transfer descriptors UhciTD *td = UhciAllocTD(hc); if (!td) { t->success = false; t->complete = true; return; } UhciTD *head = td; UhciTD *prev = 0; // Data in/out packets uint toggle = t->endp->toggle; uint packetType = TD_PACKET_IN; //Here for compiler, on some last expression hadn't worked if (t->endp->desc->addr & 0x80) packetType = TD_PACKET_IN; else packetType = TD_PACKET_OUT; uint packetSize = t->len; UhciInitTD(td, prev, speed, addr, endp, toggle, packetType, packetSize, t->data); // Initialize queue head UhciQH *qh = UhciAllocQH(hc); UhciInitQH(qh, t, head); // Schedule queue UhciInsertQH(hc, qh); if(t->w) UhciWaitForQH(hc, qh); } 

Source: https://habr.com/ru/post/hi429422/


All Articles