EHCI إنسانيا باللغة الروسية

الصورة

مقدمة


أرحب بالجميع. اليوم أريد أن أشارك تجربتي وما زلت ، في رأيي ، أشرح بوضوح عن هذا ، للوهلة الأولى ، معيارًا بسيطًا لوحدة تحكم مضيف USB 2.0.

في البداية ، يمكنك أن تتخيل أن منفذ USB 2.0 هو 4 دبابيس فقط ، اثنان منها ينقلان البيانات ببساطة (مثل ، على سبيل المثال ، منفذ COM) ، ولكن في الواقع ، كل شيء ليس كذلك ، وحتى العكس تمامًا. لا تسمح لنا وحدة تحكم USB ، من حيث المبدأ ، بنقل البيانات عن طريق منفذ COM العادي. EHCI هو معيار معقد إلى حد ما يسمح بنقل بيانات موثوق وسريع من البرنامج إلى الجهاز نفسه ، وفي الاتجاه المعاكس.

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

ما هي EHCI؟


حسنًا ، لنبدأ. EHCI - واجهة تحكم المضيف المحسنة ، تم تصميمها لنقل البيانات والتحكم في الطلبات إلى أجهزة USB ، وفي الاتجاه الآخر ، وفي 99٪ من الحالات ، يمثل رابطًا بين أي برنامج وجهاز مادي. تعمل EHCI كجهاز PCI ، وبالتالي تستخدم MMIO (Memory-Mapped-IO) للتحكم في وحدة التحكم (نعم ، أعرف أن بعض أجهزة PCI تستخدم المنافذ ، ولكن هنا قمت بتعميم كل شيء). تصف وثائق إنتل فقط مبدأ التشغيل ، ولا توجد تلميحات على الإطلاق من الخوارزميات المكتوبة على الأقل في الرمز الزائف. لدى EHCI نوعان من سجلات MMIO: القدرة والتشغيل. يعمل الأول على الحصول على خصائص جهاز التحكم ، بينما يعمل الآخر على التحكم فيه. في الواقع ، سأعلق جوهر الاتصال بين البرنامج ووحدة تحكم EHCI:

الصورة

تحتوي كل وحدة تحكم EHCI على عدة منافذ ، يمكن توصيل كل منها بأي جهاز USB. يُرجى أيضًا ملاحظة أن EHCI هي نسخة محسنة من UHCI ، والتي تم تطويرها أيضًا بواسطة Intel قبل بضع سنوات. للتوافق مع الإصدارات السابقة ، ستكون أي وحدة تحكم UHCI / OHCI تحتوي على إصدار أقل من EHCI مصاحبة لـ EHCI. على سبيل المثال ، لديك لوحة مفاتيح USB (ومعظم لوحات المفاتيح لهذا العام حتى الآن كانت كذلك) تعمل على USB 1.1 (لاحظ أن السرعة القصوى لـ USB 1.1 هي 12 ميغابت في الثانية ، و FullSpeed ​​USB 2.0 مزود بنطاق ترددي تصل إلى 480 ميجا بت في الثانية) ، ولديك جهاز كمبيوتر مزود بمنفذ USB 2.0 ، عند توصيل لوحة المفاتيح بالكمبيوتر ، ستعمل وحدة تحكم مضيف EHCI بأي شكل من الأشكال مع USB 1.1. يظهر هذا النموذج في الرسم البياني التالي:

الصورة

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

أيضًا ، دعنا نلقي نظرة على رسم تخطيطي يوضح بنية EHCI نفسها:

الصورة

على اليمين مكتوب عن قائمة الانتظار - عنهم بعد ذلك بقليل.

يسجل جهاز تحكم EHCI


بادئ ذي بدء ، أود أن أوضح مرة أخرى أنه من خلال هذه السجلات ستتحكم في جهازك ، وبالتالي فهي مهمة جدًا - وبدونها ، لا يمكن برمجة EHCI.

تحتاج أولاً إلى الحصول على عنوان MMIO المعطى لوحدة التحكم هذه ، عند الإزاحة + 0x10 سيكون عنوان سجلاتنا التي طال انتظارها. هناك شيء واحد: أولاً ، تذهب سجلات القدرة ، وبعدها فقط - التشغيل ، وبالتالي ، عند الإزاحة 0 (من العنوان السابق الذي تلقيناه عند الإزاحة 0x10 بالنسبة إلى بداية MMIO الخاص بـ EHCI) هناك بايت واحد - طول تسجيلات القدرة.

سجلات القدرة


عند الإزاحة 2 ، يوجد سجل HCIVERSION - رقم المراجعة الخاص بـ HC هذا ، والذي يأخذ 2 بايت ويحتوي على نسخة BCD من المراجعة (ما يمكن العثور عليه BCD على ويكيبيديا).
عند الإزاحة +4 ، يوجد سجل HCSPARAMS ، حجمه هو كلمتان ، ويحتوي على المعلمات الهيكلية للجهاز وتوضح بتاته ما يلي:

  • Bit 16 - مؤشرات المنفذ - مصابيح LED المتوفرة لأجهزة USB المتصلة.
  • Bits 15:12 - رقم وحدة التحكم المصاحبة المعينة لوحدة التحكم هذه
  • Bits 11: 8 - عدد المنافذ على وحدة التحكم المصاحبة
  • Bit 7 - قواعد توجيه المنفذ - يوضح كيفية تعيين هذه المنافذ إلى المنافذ المصاحبة
  • Bit 4 - التحكم في طاقة المنفذ - يشير إلى ما إذا كان من الضروري تشغيل الطاقة لكل منفذ ، 0 - يتم توفير الطاقة تلقائيًا
  • Bits 3: 0 - عدد المنافذ لوحدة التحكم هذه.
  • عند الإزاحة +8 يكمن سجل HCCPARAMS - إنه يعرض معلمات التوافق ، بتاته تعني ما يلي:
  • بت 2 - توفر طابور غير متزامن ،
  • بت 1 - توفر طابور دوري (تسلسلي)
  • بت 0 - 64 بت التوافق

سجلات العملية


عند الإزاحة 0 ، يكون سجل USBCMD هو سجل أوامر وحدة التحكم ، وتعني بتاته ما يلي:

  • Bits 23:16 - التحكم في عتبة المقاطعة - يوضح عدد الإطارات الصغيرة التي سيتم استخدامها لإطار عادي واحد. كلما كانت أكبر ، وأسرع ، ولكن إذا كان أكثر من 8 ، فستتم معالجة الإطارات الصغيرة بنفس سرعة 8.
  • بت 6 - المقاطعة بعد كل معاملة في قائمة الانتظار غير المتزامنة ،
  • بت 5 - هو قائمة الانتظار غير المتزامنة المستخدمة
  • Bit 4 - استخدام قائمة الانتظار التسلسلية ،
  • Bits 3: 2 - حجم FrameList'a (المزيد عن ذلك لاحقًا). 0 يعني 1024 عنصرًا ، 1 - 512 ، 2 - 256 ، 3 - محجوز
  • Bit 1 - تعيين لإعادة تعيين وحدة تحكم المضيف.
  • بت 0 - تشغيل / إيقاف
.
بعد ذلك ، عند الإزاحة +4 ، يوجد سجل USBSTS - حالة وحدة تحكم المضيف ،

  • يشير البت 15 إلى ما إذا كان يتم استخدام قائمة انتظار غير متزامنة.
  • يشير البت 14 إلى ما إذا كان يتم استخدام قائمة انتظار تسلسلية ،
  • بت 13 - يشير إلى أنه تم الكشف عن قائمة انتظار فارغة غير متزامنة ،
  • يتم تعيين البت 12 إلى 1 ، إذا حدث خطأ أثناء معالجة المعاملة ، فستقوم وحدة التحكم المضيفة بإيقاف جميع قوائم الانتظار.
  • يتم تعيين بت 4 إلى 1 ، إذا حدث خطأ فادح ، توقف وحدة تحكم المضيف جميع قوائم الانتظار.
  • Bit 3 FrameList (Register) Rollover - يتم ضبطه على 1 عندما تقوم وحدة تحكم المضيف بمعالجة قائمة الإطارات بأكملها.
  • بت 1 - مقاطعة خطأ USB - هل أقوم بإنشاء مقاطعة خطأ؟
  • بت 0 - مقاطعة USB - يتم تعيينه بعد معالجة المعاملات الناجحة ، إذا تم تثبيت بطاقة IOC في TD

لا تعب؟ يمكنك صب طيور النورس القوية وإحضار الكبد ، نحن في البداية!

عند الإزاحة +8 ، يوجد تسجيل USBINTR - سجل تمكين المقاطعة
لكي لا تكتب لفترة طويلة ، والأكثر من ذلك ، حتى لا تقرأ لفترة طويلة ، يمكن العثور على قيم بتات هذا السجل في المواصفات ، وسيتم ترك رابط لها أدناه. هنا فقط أكتب 0 ، لأن ليس لدي أي رغبة على الإطلاق في كتابة معالجات ، ومقاطعات الخريطة ، وما إلى ذلك ، لذلك أعتقد أن هذا لا معنى له على الإطلاق.

عند الإزاحة +12 (0x0C) ، يوجد سجل FRINDEX ، حيث يكمن رقم الإطار الحالي ببساطة ، وأريد أن أشير إلى أن البتات الأربعة الأخيرة تظهر رقم الإطار الصغير ، في 28 بت العلوي رقم الإطار (القيمة ليست بالضرورة أصغر من حجم frameList ولكن إذا كنت بحاجة إلى فهرس ، فمن الأفضل أن تأخذه بقناع 0x3FF (أو 0x1FF ، وما إلى ذلك).

يكون سجل CTRLDSSEGMENT في الإزاحة + 0x10 ؛ ويظهر على وحدة تحكم المضيف أهم 32 بتًا من عنوان ورقة الإطار.

يحتوي سجل PERIODICLISTBASE على إزاحة قدرها + 0x14 ، يمكنك وضع 32 بتًا أقل من ورقة الإطار فيه ، لاحظ أنه يجب محاذاة العنوان مع حجم صفحة الذاكرة (4096).

يحتوي سجل ASYNCLISTADDR على إزاحة قدرها + 0x18 ، يمكنك وضع عنوان قائمة الانتظار غير المتزامنة فيه ، لاحظ أنه يجب محاذاة عند حد 32 بايت ، بينما يجب أن يكون في أول أربعة غيغابايت من الذاكرة الفعلية.

يشير تسجيل CONFIGFLAG إلى ما إذا تم تكوين الجهاز. يجب تعيين بت 0 بعد الانتهاء من إعداد الجهاز ، مع إزاحة + 0x40.

دعنا ننتقل إلى تسجيلات المنفذ. يحتوي كل منفذ على سجل حالة الأمر الخاص به ، ويتم إزاحة كل سجل منفذ + 0x44 + (رقم PortNumber - 1) * 4 ، بتاته تعني ما يلي:

  • Bit 12 - طاقة المنفذ ، 1 - يتم توفير الطاقة ، 0 - لا.
  • تم تعيين Bit 8 - Port Rest - لإعادة تعيين الجهاز.
  • Bit 3 - تمكين / تعطيل المنفذ - يتم تعيينه عند تغيير حالة "تضمين" المنفذ.
  • بت 2 - تشغيل / إيقاف المنفذ.
  • Bit 1 - تغيير حالة الاتصال ، يتم تعيينه على 1 ، على سبيل المثال ، إذا قمت بتوصيل جهاز USB أو فصله.
  • بت 0 - حالة الاتصال ، 1 - متصل ، 0 - لا.

الآن دعنا ننتقل إلى العصير نفسه.

نقل البيانات وهياكل الاستعلام


يتضمن تنظيم بنية معالجة الطلبات قوائم الانتظار واصفات النقل (TDs).

في الوقت الحالي ، سننظر فقط في 3 هياكل.

قائمة متسلسلة


يتم تنظيم القائمة التسلسلية (الدورية ، Pereodic) على النحو التالي:

الصورة

كما ترى في الرسم البياني ، تبدأ المعالجة بالحصول على الإطار المطلوب من إطار الورقة ، حيث يشغل كل عنصر من عناصره 4 بايت وله البنية التالية:

الصورة

كما ترى في الصورة ، يتم محاذاة نقل قائمة الانتظار / الواصف عند حد 32 بايت ، يعني البت 0 أن وحدة تحكم المضيف لن تعالج هذا العنصر ، وتشير البتات 3: 1 إلى نوع ما ستتعامل معه وحدة التحكم المضيفة: 0 - TD متزامن (iTD) ، 1 - بدوره ، 2 و 3 في هذه المقالة لن أفكر.

قائمة انتظار غير متزامنة


تقوم وحدة تحكم المضيف بمعالجة قائمة الانتظار هذه فقط عندما يكون الإطار المتسلسل فارغًا أو عندما تقوم وحدة تحكم المضيف بمعالجة القائمة التسلسلية بالكامل.

قائمة الانتظار غير المتزامنة هي مؤشر لقائمة انتظار تحتوي على قوائم انتظار أخرى تحتاج إلى معالجة. مخطط:

الصورة

qTD (واصف نقل عنصر قائمة الانتظار)


هذا TD لديه الهيكل التالي:

الصورة

مؤشر qTD التالي - مؤشر استمرار قائمة الانتظار للمعالجة (للتنفيذ الأفقي) ، بت 0 يشير مؤشر qTD التالي إلى عدم وجود قائمة انتظار أخرى.
رمز qTD - الرمز المميز لـ TD ، يعرض معلمات نقل البيانات:

  • Bit 31 - تبديل البيانات (المزيد عن ذلك لاحقًا)
  • بت 30:16 - كمية البيانات المراد نقلها ، بعد إتمام المعاملة ، تنخفض قيمتها بمقدار البيانات المنقولة.
  • بت 15 - IOC - المقاطعة عند اكتمالها - تسبب المقاطعة بعد اكتمال معالجة الواصف.
  • توضح البتات 14:12 عدد المخزن المؤقت الحالي إلى / الذي يتم تبادل البيانات منه ، المزيد عن ذلك لاحقًا.
  • Bits 11:10 - عدد الأخطاء المسموح بها. يوضح هذا الجدول عندما ينخفض ​​عدد الأخطاء:

    الصورة

    الحاشية السفلية 1 - يؤدي الكشف عن Babble أو Stall تلقائيًا إلى إيقاف تنفيذ رأس قائمة الانتظار. الحاشية السفلية 3 - أخطاء المخزن المؤقت للبيانات هي مشاكل مع المضيف. لا تأخذ في الاعتبار عمليات إعادة محاولة الجهاز.
  • 9: 8 - رمز PID - نوع الرمز المميز: 0 - الرمز المميز للإدخال (من المضيف إلى الجهاز) ، 1 - الرمز المميز للإخراج (من الجهاز إلى المضيف) ، 2 - الرمز "SETUP"
  • البتات 7: 0 تشير إلى حالة TD:
    تشير البتة 7 إلى أن TD في حالة نشطة (بمعنى أن وحدة تحكم المضيف تعالج TD)
    بت 6 - تم الإيقاف - يشير إلى حدوث خطأ وتوقف تنفيذ TD.
    Bit 4 - Babble Detected - كمية البيانات التي أرسلناها إلى الجهاز ، أو لكل ثورة ، أقل مما نرسله ، على سبيل المثال ، أرسل لنا الجهاز 100 بايت من البيانات ، ونقرأ 50 بايت فقط ، ثم 50 بايت أخرى سيتم أيضًا تعيين البت المُوقوف إذا تم تعيين هذا البت إلى 1.
    بت 3 - خطأ في المعاملة - حدث خطأ أثناء المعاملة.

قائمة مؤشر صفحة المخزن المؤقت qTD - أي من 5 مخازن. يحتوي على رابط إلى المكان الذي يجب أن تتم فيه المعاملة في الذاكرة (إرسال البيانات إلى الجهاز / استقبال البيانات من الجهاز) ، يجب محاذاة جميع العناوين في المخازن المؤقتة ، باستثناء العنوان الأول ، مع حجم الصفحة (4096 بايت).

رأس الخط


يحتوي رأس قائمة الانتظار على الهيكل التالي:

الصورة

مؤشر الارتباط الأفقي لرأس قائمة الانتظار - المؤشر إلى قائمة الانتظار التالية ، تحتوي البتات 2: 1 على القيم التالية اعتمادًا على نوع قائمة الانتظار:

الصورة

قدرات / خصائص نقطة النهاية - خصائص قائمة الانتظار:

  • تحتوي البتات 26:16 على الحد الأقصى لحجم الحزمة للإرسال
  • Bit 14: التحكم في تبديل البيانات - يوضح المكان الذي يجب أن تأخذ فيه وحدة تحكم المضيف قيمة تبديل البيانات الأولية ، 0 - يتجاهل بت DT في qTD ، ويحفظ بت DT لرأس قائمة الانتظار.
  • Bit 13:12 - خصائص معدل الإرسال: الصورة
  • Bits 11: 8 - رقم نقطة النهاية التي تم تقديم الطلب إليها
  • بت 6: 0 - عنوان الجهاز

إمكانات نقطة النهاية: Head Queue Head DWord 2 - استمرار الكلمة المزدوجة السابقة:

  • بت 29:23 - رقم المحور
  • بت 22:16 - عنوان المحور

مؤشر ارتباط qTD الحالي - المؤشر إلى qTD الحالي.

ننتقل إلى الأكثر إثارة للاهتمام.

سائق EHCI


لنبدأ بالاستفسارات التي يمكن لـ EHCI الإجابة عليها. هناك نوعان من الطلبات: Control - a la command و Bulk - إلى نقاط النهاية ، لتبادل البيانات ، على سبيل المثال ، تستخدم الغالبية العظمى من محركات أقراص USB المحمولة (USB MassStorage) نوع نقل البيانات Bulk / Bulk / Bulk. يستخدم الماوس ولوحة المفاتيح أيضًا الطلبات المجمعة لنقل البيانات.

تهيئة EHCI وتكوين قوائم انتظار غير متزامنة ومتسلسلة:

// Base I/O Address PciBar bar; PciGetBar(&bar, id, 0); EhciController *hc = VMAlloc(sizeof(EhciController)); hc->capRegs = (EhciCapRegs *)(uintptr_t)bar.u.address; hc->opRegs = (EhciOpRegs *)(uintptr_t)(bar.u.address + hc->capRegs->capLength); // Read the Command register //    uint cmd = ROR(usbCmdO); // Write it back, setting bit 2 (the Reset bit) //   ,   2(Reset) // and making sure the two schedule Enable bits are clear. //  ,  2   WOR(usbCmdO, 2 | cmd & ~(CMD_ASE | CMD_PSE)); // A small delay here would be good. You don't want to read //     ,     // the register before it has a chance to actually set the bit //   ,         ROR(usbCmdO); // Now wait for the controller to clear the reset bit. //      Reset while (ROR(usbCmdO) & 2); // Again, a small delay here would be good to allow the // reset to actually become complete. //   ROR(usbCmdO); // wait for the halted bit to become set //    Halted    while (!(ROR(usbStsO) & STS_HCHALTED)); //     ,        // ,           128  hc->frameList = (u32 *)VMAlloc(1024 * sizeof(u32) + 8192 * 4); hc->frameList = (((uint)hc->frameList) / 16384) * 16384 + 16384; hc->qhPool = (EhciQH *)VMAlloc(sizeof(EhciQH) * MAX_QH + 8192 * 4); hc->tdPool = (EhciTD *)VMAlloc(sizeof(EhciTD) * MAX_TD + 8192 * 4); hc->qhPool = (((uint)hc->qhPool) / 16384) * 16384 + 16384; hc->tdPool = (((uint)hc->tdPool) / 16384) * 16384 + 16384; // Asynchronous queue setup //    EhciQH *qh = EhciAllocQH(hc); //     ,      // ,    qh->qhlp = (u32)(uintptr_t)qh | PTR_QH; //  ,  ,     qh->ch = QH_CH_H; qh->caps = 0; qh->curLink = 0; qh->nextLink = PTR_TERMINATE; qh->altLink = 0; qh->token = 0; //    for (uint i = 0; i < 5; ++i) { qh->buffer[i] = 0; qh->extBuffer[i] = 0; } hc->asyncQH = qh; // Periodic list queue setup //    qh = EhciAllocQH(hc); //     qh->qhlp = PTR_TERMINATE; qh->ch = 0; qh->caps = 0; qh->curLink = 0; qh->nextLink = PTR_TERMINATE; qh->altLink = 0; qh->token = 0; //   for (uint i = 0; i < 5; ++i) { qh->buffer[i] = 0; qh->extBuffer[i] = 0; } qh->transfer = 0; qh->qhLink.prev = &qh->qhLink; qh->qhLink.next = &qh->qhLink; hc->periodicQH = qh; //        for (uint i = 0; i < 1024; ++i) hc->frameList[i] = PTR_QH | (u32)(uintptr_t)qh; kprintf("FrameList filled. Turning off Legacy BIOS support..."); // Check extended capabilities //  BIOS Legacy support uint eecp = (RCR(hccParamsO) & HCCPARAMS_EECP_MASK) >> HCCPARAMS_EECP_SHIFT; if (eecp >= 0x40) { // Disable BIOS legacy support uint legsup = PciRead32(id, eecp + USBLEGSUP); kprintf("."); if (legsup & USBLEGSUP_HC_BIOS) { PciWrite32(id, eecp + USBLEGSUP, legsup | USBLEGSUP_HC_OS); kprintf("."); for (;;) { legsup = PciRead32(id, eecp + USBLEGSUP); kprintf("."); if (~legsup & USBLEGSUP_HC_BIOS && legsup & USBLEGSUP_HC_OS) { break; } } } } kprintf("Done\n"); // Disable interrupts //   //hc->opRegs->usbIntr = 0; MWIR(ehcibase, usbIntrO, 0); // Setup frame list //     //hc->opRegs->frameIndex = 0; WOR(frameIndexO, 0); //hc->opRegs->periodicListBase = (u32)(uintptr_t)hc->frameList; WOR(periodicListBaseO, (u32)(uintptr_t)hc->frameList); //       //hc->opRegs->asyncListAddr = (u32)(uintptr_t)hc->asyncQH; WOR(asyncListAddrO, (u32)(uintptr_t)hc->asyncQH); //    0 //hc->opRegs->ctrlDsSegment = 0; WOR(ctrlDsSegmentO, 0); // Clear status //   //hc->opRegs->usbSts = ~0; WOR(usbStsO, ~0); // Enable controller //  , 8 -,  //     //hc->opRegs->usbCmd = (8 << CMD_ITC_SHIFT) | CMD_PSE | CMD_ASE | CMD_RS; WOR(usbCmdO, (8 << CMD_ITC_SHIFT) | CMD_PSE | CMD_ASE | CMD_RS); while (ROR(usbStsO)&STS_HCHALTED); // Configure all devices to be managed by the EHCI // ,   //hc->opRegs->configFlag = 1; WOR(configFlagO, 1);\ // Probe devices //   EhciProbe(hc); 

في الواقع ، رمز إعادة تعيين المنفذ إلى حالته الأصلية:

  volatile u32 *reg = &hc->opRegs->ports[port]; //    ,  100 *reg|=(1<<12)|(1<<20); Wait(100); //  ,  50  EhciPortSet(reg, PORT_RESET | (1<<12) | (1<<20) | (1<<6)); Wait(50); EhciPortClr(reg, PORT_RESET); // Wait 100ms for port to enable (TODO - what is appropriate length of time?) //  100    ,   , //  100    uint status = 0; for (uint i = 0; i < 10; ++i) { // Delay Wait(10); // Get current status //    status = *reg; // Check if device is attached to port //      if (~status & PORT_CONNECTION) break; // Acknowledge change in status //    -    if (status & (PORT_ENABLE_CHANGE | PORT_CONNECTION_CHANGE)) { EhciPortClr(reg, PORT_ENABLE_CHANGE | PORT_CONNECTION_CHANGE); continue; } // Check if device is enabled //    ,    if (status & PORT_ENABLE) break; } return status; 

طلب التحكم بالجهاز:

 static void EhciDevControl(UsbDevice *dev, UsbTransfer *t) { EhciController *hc = (EhciController *)dev->hc; UsbDevReq *req = t->req; // Determine transfer properties //    uint speed = dev->speed; uint addr = dev->addr; uint maxSize = dev->maxPacketSize; uint type = req->type; uint len = req->len; // Create queue of transfer descriptors //   TDs EhciTD *td = EhciAllocTD(hc); if (!td) return; EhciTD *head = td; EhciTD *prev = 0; // Setup packet //   uint toggle = 0; uint packetType = USB_PACKET_SETUP; uint packetSize = sizeof(UsbDevReq); EhciInitTD(td, prev, toggle, packetType, packetSize, req); prev = td; // Data in/out packets packetType = type & RT_DEV_TO_HOST ? USB_PACKET_IN : USB_PACKET_OUT; u8 *it = (u8 *)t->data; u8 *end = it + len; //EhciPrintTD(td); while (it < end) { td = EhciAllocTD(hc); if (!td) return; toggle ^= 1; packetSize = end - it; if (packetSize > maxSize) packetSize = maxSize; EhciInitTD(td, prev, toggle, packetType, packetSize, it); it += packetSize; prev = td; } // Status packet //   td = EhciAllocTD(hc); if (!td) return; toggle = 1; packetType = type & RT_DEV_TO_HOST ? USB_PACKET_OUT : USB_PACKET_IN; EhciInitTD(td, prev, toggle, packetType, 0, 0); // Initialize queue head //   : EhciQH *qh = EhciAllocQH(hc); EhciInitQH(qh, t, head, dev->parent, false, speed, addr, 0, maxSize); // Wait until queue has been processed //       EhciInsertAsyncQH(hc->asyncQH, qh); EhciWaitForQH(hc, qh); } 

رمز معالجة قائمة الانتظار:

  if (qh->token & TD_TOK_HALTED) { t->success = false; t->complete = true; } else if (qh->nextLink & PTR_TERMINATE) if (~qh->token & TD_TOK_ACTIVE) { if (qh->token & TD_TOK_DATABUFFER) kprintf(" Data Buffer Error\n"); if (qh->token & TD_TOK_BABBLE) kprintf(" Babble Detected\n"); if (qh->token & TD_TOK_XACT) kprintf(" Transaction Error\n"); if (qh->token & TD_TOK_MMF) kprintf(" Missed Micro-Frame\n"); t->success = true; t->complete = true; } if (t->complete) .... 

والآن طلب نقطة النهاية (طلب مجمع)

 static void EhciDevIntr(UsbDevice *dev, UsbTransfer *t) { EhciController *hc = (EhciController *)dev->hc; // Determine transfer properties //    uint speed = dev->speed; uint addr = dev->addr; uint maxSize = t->endp->desc->maxPacketSize; uint endp = t->endp->desc->addr & 0xf; EhciTD *td = EhciAllocTD(hc); if (!td) { t->success = false; t->complete = true; return; } EhciTD *head = td; EhciTD *prev = 0; // Data in/out packets uint toggle = t->endp->toggle; uint packetType = t->endp->desc->addr & 0x80 ? USB_PACKET_IN : USB_PACKET_OUT; uint packetSize = t->len; EhciInitTD(td, prev, toggle, packetType, packetSize, t->data); // Initialize queue head //    EhciQH *qh = EhciAllocQH(hc); EhciInitQH(qh, t, head, dev->parent, true, speed, addr, endp, maxSize); //printQh(qh); // Schedule queue //    EhciInsertPeriodicQH(hc->periodicQH, qh); } 

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

أرصفة: مواصفات

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


All Articles