UHCI ، أو أول USB



يوم جيد عزيزي القارئ! طُلب مني أن أكتب عن UHCI - حسنًا ، أنا أكتب.

قد تجد هذه المقالة مفيدة إذا لم تكن لديك ، على سبيل المثال ، مهارات كتابة كافية لبرامج التشغيل وقراءة وثائق أحد الأجهزة. مثال بسيط: تريد كتابة نظام التشغيل الخاص بك لجهاز كمبيوتر صغير ، بحيث لا تقوم بعض أنظمة تشغيل Windows أو توزيعات Linux الأخرى بتنزيل الحديد ، وأنت تستخدم كل طاقتها حصريًا لأغراضك الخاصة.

ما هو UHCI؟


أعتقد أنه ، حتى لا ترش مرة أخرى حول موضوع ماذا ولماذا ، فقط اترك رابطًا لمقالتي السابقة حول EHCI. كزة هنا
UHCI - تعمل واجهة وحدة تحكم المضيف العالمي كجهاز PCI ، ولكن بخلاف EHCI تستخدم المنافذ بدلاً من MMIO (Memory-Mapped-IO).



المصطلحات الواجب استخدامها فيما بعد


  • برنامج تشغيل USB (USBD) - برنامج تشغيل USB نفسه
  • HC (وحدة تحكم المضيف) - وحدة تحكم مضيف ، أو UHCI فقط
  • برنامج تشغيل وحدة تحكم المضيف (HCD) - برنامج يقوم بتوصيل الأجهزة و USBD
  • جهاز USB - جهاز USB نفسه

أنواع نقل البيانات


متزامن - الإرسال المتزامن ، الذي له تردد معين لنقل البيانات. يمكن استخدامه ، على سبيل المثال ، لميكروفونات USB ، إلخ.

المقاطعة - عمليات نقل البيانات الصغيرة والعفوية من الجهاز. يدعم نوع إرسال المقاطعة الأجهزة التي تتطلب فترة خدمة يمكن التنبؤ بها ولكنها لا توفر بالضرورة دفق بيانات يمكن التنبؤ به. يشيع استخدامه للأجهزة مثل لوحات المفاتيح وأجهزة التأشير التي قد لا توفر بيانات لفترات زمنية طويلة ، ولكنها تتطلب استجابة سريعة عندما يكون لديهم بيانات لإرسالها.

التحكم - نوع إرسال المعلومات حول حالة الجهاز وحالته وتكوينه. يتم استخدام نوع نقل التحكم لتوفير قناة تحكم من Host إلى أجهزة USB. تتكون إرسالات التحكم دائمًا من مرحلة الإعداد و صفر أو أكثر من مراحل البيانات متبوعة بمرحلة الحالة. من الضروري معالجة نقل التحكم إلى نقطة نهاية معينة في وضع FIFO. إذا تم تمرير التحكم إلى نفس نقطة النهاية ، فقد يؤدي التشذير إلى سلوك غير متوقع.

كتلة - نوع من نقل صفائف البيانات. تستخدم ، على سبيل المثال ، في أجهزة MassStorage.



هذه هي الطريقة التي يبدو بها توزيع الوقت بمقدار 1 مللي ثانية - معالجة إطار واحد.

توزيع الوقت


تدعم وحدة التحكم المضيفة تسليم البيانات في الوقت الفعلي عن طريق إنشاء حزمة Start Of Frame (SOF) كل 1 مللي ثانية. يتم إنشاء حزمة SOF عند انتهاء صلاحية عداد SOF في وحدة تحكم المضيف (الشكل 3). يقوم جهاز التحكم المضيف بتهيئة عداد SOF لمدة إطار 1 مللي ثانية. يمكن إجراء تغييرات بسيطة على هذه القيمة (وبالتالي الفترة الزمنية للإطار) من خلال برمجة سجل تغيير SOF. تسمح لك هذه الميزة بإجراء تغييرات طفيفة على الفترة الزمنية للإطار ، إذا لزم الأمر ، للحفاظ على التزامن في الوقت الحقيقي عبر نظام USB بالكامل.

تتضمن وحدة التحكم المضيفة رقم الإطار في كل حزمة SOF. يحدد رقم الإطار هذا بشكل فريد فترة الإطار في الوقت الفعلي. تحدث نهاية حالة الإطار (EOF) في نهاية الفترة الزمنية البالغة 1 مللي ثانية عندما يبدأ جهاز التحكم المضيف وقت الإطار التالي ، مما يؤدي إلى إنشاء حزمة SOF أخرى برقم الإطار المقابل. خلال فترة الإطار ، يتم إرسال البيانات كحزم معلومات. يتم فرض الفترة الزمنية للإطار بشكل صارم بواسطة وحدة تحكم المضيف ، ولا يمكن أن تتجاوز حزم البيانات في الإطار الحالي EOF (راجع الفصل 11 في مواصفات USB). تدعم وحدة التحكم المضيفة مزامنة إرسال البيانات بين الإطارات في الوقت الفعلي ، وربط رقم الإطار لإجراء إدخال محدد في قائمة الإطارات. يقوم عداد إطار وحدة تحكم المضيف بإنشاء رقم إطار (قيمة 11 بت) وإدراجه في كل حزمة SOF. يتم برمجة العداد من خلال السجلات وكل فترة إطار تزداد. يستخدم جهاز التحكم المضيف 10 بتات أقل من رقم الإطار كفهرس في قائمة الإطارات مع 1024 إطارًا ، والتي يتم تخزينها في ذاكرة النظام. وبالتالي ، نظرًا لأن عداد الإطار يتحكم في تحديد إدخال من قائمة الإطارات ، فإن وحدة تحكم المضيف تقوم بمعالجة كل إدخال في القائمة في فترة إطار معينة. يتم توسيع وحدة تحكم المضيف إلى الإدخال التالي في قائمة الإطارات لكل إطار جديد. وهذا يضمن تنفيذ عمليات الإرسال المتزامنة في إطار معين.

الشكل 3:



هيكل UHCI


كل شيء تمامًا كما هو الحال مع EHCI. طلبات مثال لـ HC:



تكوين والوصول إلى UHCI


وهكذا ، كما قلت سابقًا ، تعمل UHCI من خلال المنافذ ، لذا من PCI نحتاج إلى معرفة قاعدة تسجيلات UHCI.



عند الإزاحة 0x20 هناك 4 بايت - قاعدة IO. فيما يتعلق بقاعدة الإدخال / الإخراج ، يمكننا استخدام السجلات التالية:



سجلات UHCI


  • USBCMD هو سجل للتحكم في HC. البتات:
    • Bit 6 هو علامة على أنه تم تكوين الجهاز وتهيئته بنجاح.
    • بت 1 - إعادة تعيين HC. تعيين لإعادة HC.
    • بت 0 - تشغيل / إيقاف. يعرض حالة HC. 1 - يعمل ، 0 - لا.
  • USBSTS - تسجيل الحالة. البتات:
    • بت 5 - توقف HC. حدث خطأ ، أو أكملت وحدة التحكم بنجاح إعادة تعيين HC.
    • بت 4 - خطأ في عملية تحكم المضيف. يتم تعيين البت إلى 1 عند حدوث خطأ فادح ولا يمكن HC متابعة قائمة الانتظار و TD.
    • بت 3 - خطأ في النظام المضيف. خطأ PCI.
    • بت 1 - مقاطعة الخطأ. يشير إلى حدوث خطأ وتولد HC مقاطعة.
    • بت 0 - المقاطعة. يشير إلى أن HC أدى إلى مقاطعة.
  • USBINTR - تسجيل إعدادات المقاطعة. البتات:
    • بت 2 - IOC - المقاطعة عند اكتمالها - تنشئ مقاطعة في نهاية المعاملة.
  • FRNUM - رقم الإطار الحالي (خذ & 0x3FF للقيمة الصحيحة).
  • FLBASEADD - العنوان الأساسي لقائمة الإطارات - عنوان قائمة الإطارات.
  • PORTSC - حالة الميناء والتحكم فيه - سجل الحالة ومراقبة المنفذ. البتات:
    • بت 9 - إعادة تعيين المنفذ - 1 - منفذ لإعادة التعيين.
    • بت 8 - يشير إلى أن جهاز منخفض السرعة متصل بالمنفذ
    • بت 3 - يشير إلى أن المنفذ في حالة تغير
    • Bit 2 - يشير إلى ما إذا كان المنفذ ممكّنًا
    • Bit 1 - يشير إلى أن حالة الجهاز متصلة بالمنفذ
    • بت 0 - يشير إلى أن الجهاز متصل بالمنفذ.

الهياكل


مؤشر قائمة الإطارات




نقل واصف




TD التحكم وحالة
. البتات:
  • بت 28-27 - عداد الخطأ ، على غرار EHCI.
    • بت 26-1 = جهاز منخفض السرعة ، 0 = جهاز كامل السرعة.
    • بت 25-1 = TD متزامن
    • بت 24 - اللجنة الأولمبية الدولية
    • بت 23-16 - الحالة:
    • بت 23 - يشير إلى أنه TD نشط
    • بت 22 - متوقف
    • بت 21 - خطأ مخزن البيانات
    • بت 20 - تم اكتشاف بابل
    • بت 19 - NAK
  • البتات 10–0: عدد وحدات البايت المرسلة بواسطة وحدة تحكم المضيف.

رمز TD

  • بت 31:21 - ماكس باكيت لين ، على غرار EHCI
  • بت 19 - تبديل البيانات ، على غرار EHCI
  • بت 18:15 - رقم نقطة النهاية
  • بت 18:14 - عنوان الجهاز
  • بت 7: 0 - PID. In = 0x69 ، Out = 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/ar429422/


All Articles