EHCI secara manusiawi dalam bahasa Rusia

gambar

Pendahuluan


Saya menyambut semua orang. Hari ini saya ingin berbagi pengalaman saya dan masih, menurut pendapat saya, jelas menjelaskan tentang itu, pada pandangan pertama, standar sederhana untuk pengontrol host USB 2.0.

Awalnya, Anda dapat membayangkan bahwa port USB 2.0 hanya 4 pin, dua di antaranya hanya mentransmisikan data (Seperti, misalnya, port COM), tetapi pada kenyataannya, semuanya tidak begitu, dan bahkan sebaliknya. Pengontrol USB, pada prinsipnya, tidak memungkinkan kami untuk mentransfer data melalui port COM biasa. EHCI adalah standar yang agak canggih yang memungkinkan transfer data yang andal dan cepat dari perangkat lunak ke perangkat itu sendiri, dan dalam arah yang berlawanan.

Anda mungkin menemukan artikel ini berguna jika, misalnya, Anda tidak memiliki keterampilan menulis yang cukup untuk driver dan membaca dokumentasi untuk perangkat keras. Contoh sederhana: Anda ingin menulis OS untuk mini-PC, sehingga beberapa Windows atau distribusi Linux lainnya tidak mengunduh perangkat keras, dan Anda menggunakan semua kekuatannya secara eksklusif untuk keperluan Anda sendiri.

Apa itu EHCI?


Baiklah, mari kita mulai. EHCI - Enhanced Host Controller Interface, dirancang untuk mentransfer data dan mengontrol permintaan ke perangkat USB, dan ke arah lain, dan dalam 99% kasus, ini merupakan tautan antara perangkat lunak apa pun dan perangkat fisik. EHCI berfungsi sebagai perangkat PCI, dan karenanya menggunakan MMIO (Memory-Mapped-IO) untuk mengontrol pengontrol (ya, saya tahu bahwa beberapa perangkat PCI menggunakan porta, tapi di sini saya menggeneralisasi semuanya). Dokumentasi dari Intel hanya menjelaskan prinsip operasi, dan tidak ada petunjuk sama sekali tentang algoritma yang ditulis setidaknya dalam pseudo-code. EHCI memiliki 2 jenis register MMIO: Kemampuan dan Operasional. Yang pertama berfungsi untuk mendapatkan karakteristik controller, sedangkan yang kedua berfungsi untuk mengendalikannya. Sebenarnya, saya akan melampirkan esensi koneksi antara perangkat lunak dan pengontrol EHCI:

gambar

Setiap pengontrol EHCI memiliki beberapa port, yang masing-masing dapat dihubungkan ke perangkat USB apa pun. Perlu diketahui juga bahwa EHCI adalah versi perbaikan dari UHCI, yang juga dikembangkan oleh Intel beberapa tahun sebelumnya. Untuk kompatibilitas mundur, setiap pengontrol UHCI / OHCI yang memiliki versi lebih rendah dari EHCI akan menjadi pendamping EHCI. Misalnya, Anda memiliki keyboard USB (dan sebagian besar keyboard tahun ini seperti itu) yang bekerja pada USB 1.1 (perhatikan bahwa kecepatan maksimum USB 1.1 adalah 12 megabit per detik, dan FullSpeed ​​USB 2.0 memiliki bandwidth sebanyak 480 Mbps), dan Anda memiliki komputer dengan port USB 2.0, saat Anda menyambungkan keyboard ke komputer, pengontrol host EHCI entah bagaimana akan bekerja dengan USB 1.1. Model ini ditunjukkan pada diagram berikut:

gambar

Juga, untuk masa depan saya ingin segera memperingatkan Anda bahwa driver Anda mungkin tidak berfungsi dengan benar karena situasi yang absurd: Anda menginisialisasi UHCI, dan kemudian EHCI, sambil menambahkan dua perangkat yang sama, mengatur bit Kontrol Pemilik Port ke register port, dan kemudian UHCI berhenti bekerja, karena EHCI secara otomatis menyeret port ke dirinya sendiri, dan port pada UHCI berhenti merespons, situasi ini perlu dipantau.

Juga, mari kita lihat diagram yang menunjukkan arsitektur EHCI itu sendiri:

gambar

Di sebelah kanan ditulis tentang antrian - tentang mereka sedikit kemudian.

Register pengendali EHCI


Untuk mulai dengan, saya ingin mengklarifikasi sekali lagi bahwa melalui register ini Anda akan mengontrol perangkat Anda, oleh karena itu mereka sangat penting - dan tanpa mereka pemrograman EHCI tidak mungkin.

Pertama, Anda perlu mendapatkan alamat MMIO yang diberikan kepada pengontrol ini, di offset + 0x10 akan menjadi alamat register lama kami. Ada satu hal: pertama, register Kemampuan pergi, dan hanya setelah mereka - Operasional, oleh karena itu, pada offset 0 (dari alamat sebelumnya, yang kami terima pada offset 0x10 relatif terhadap awal MMIO EHCI kami), ada satu byte - panjang register Kemampuan.

Register kemampuan


Pada offset 2, register HCIVERSION berada - nomor revisi HC ini, yang menempati 2 byte dan berisi versi revisi BCD (BCD apa yang dapat ditemukan di Wikipedia).
Pada offset +4, register HCSPARAMS berada , ukurannya 2 kata, berisi parameter struktural perangkat dan bitnya menunjukkan yang berikut:

  • Bit 16 - Indikator Port - LED yang tersedia untuk perangkat USB yang terhubung.
  • Bit 15:12 - jumlah pengontrol pengiring yang ditugaskan untuk pengontrol ini
  • Bit 11: 8 - jumlah port pada pengontrol pendamping
  • Bit 7 - Aturan Routing Port - menunjukkan bagaimana port ini dipetakan ke port pendamping
  • Bit 4 - Port Power Control - menunjukkan apakah perlu menyalakan daya untuk setiap port, 0 - daya disediakan secara otomatis
  • Bits 3: 0 - jumlah port untuk pengontrol ini.
  • Pada offset +8 terletak register HCCPARAMS - ia menunjukkan parameter kompatibilitas, bitnya berarti sebagai berikut:
  • Bit 2 - ketersediaan antrian sinkron,
  • Bit 1 - ketersediaan antrian periodik (berurutan)
  • Kompatibilitas bit 0 - 64 bit

Register operasi


Pada offset 0, register USBCMD adalah register perintah controller, bit-bitnya berarti sebagai berikut:

  • Bit 23:16 - Interrupt Threshold Control - menunjukkan berapa banyak mikro-frame yang akan digunakan untuk satu frame biasa. Semakin banyak, semakin cepat, tetapi jika lebih dari 8, maka mikro-frame akan diproses pada kecepatan yang sama seperti untuk 8.
  • Bit 6 - interupsi setelah setiap transaksi dalam antrian asinkron,
  • Bit 5 - adalah antrian asinkron yang digunakan
  • Bit 4 - penggunaan antrian berurutan,
  • Bit 3: 2 - ukuran FrameList'a (lebih lanjut tentang itu nanti). 0 berarti 1024 elemen, 1 - 512, 2 - 256, 3 - dicadangkan
  • Bit 1 - Set untuk mengatur ulang pengontrol host.
  • Bit 0 - Run / Stop
.
Selanjutnya, pada offset +4, ada register USBSTS - status pengontrol host,

  • Bit 15 menunjukkan apakah antrian asinkron digunakan.
  • Bit 14 menunjukkan apakah antrian berurutan sedang digunakan,
  • Bit 13 - menunjukkan bahwa antrian asinkron kosong telah terdeteksi,
  • Bit 12 diatur ke 1, jika terjadi kesalahan saat memproses transaksi, maka pengontrol host akan menghentikan semua antrian.
  • Bit 4 diatur ke 1, jika kesalahan serius terjadi, pengontrol host menghentikan semua antrian.
  • Bit 3 FrameList (Register) Rollover - atur ke 1 ketika pengontrol host memproses seluruh frameList.
  • Bit 1 - USB Error Interrupt - Apakah saya menghasilkan interupsi kesalahan?
  • Bit 0 - USB Interrupt - ditetapkan setelah pemrosesan transaksi berhasil, jika IOC diinstal pada TD

Tidak lelah? Anda dapat menuangkan burung camar yang kuat dan membawa hati, kami berada di awal!

Pada offset +8, ada register USBINTR - interrupt enable register
Agar tidak menulis untuk waktu yang lama, dan lebih dari itu, agar Anda tidak membaca untuk waktu yang lama, nilai bit register ini dapat ditemukan dalam spesifikasi, tautan ke sana akan tertinggal di bawah. Di sini saya hanya menulis 0, karena Saya sama sekali tidak memiliki keinginan untuk menulis penangan, interupsi peta, dll., Jadi ini saya pikir hampir tidak ada gunanya.

Pada offset +12 (0x0C), register FRINDEX berada , di mana nomor frame saat ini terletak, dan saya ingin mencatat bahwa 4 bit terakhir menunjukkan nomor frame mikro, di bagian atas 28 bit nomor frame (nilainya juga belum tentu lebih kecil dari ukuran frameList) Tetapi jika Anda membutuhkan indeks, lebih baik membawanya dengan mask 0x3FF (atau 0x1FF, dll.).

Register CTRLDSSEGMENT berada pada offset + 0x10, ini menunjukkan pengendali host 32 bit paling signifikan dari alamat frame sheet.

Register PERIODICLISTBASE memiliki offset + 0x14, Anda dapat memasukkan 32 bit frame sheet yang lebih rendah ke dalamnya, perhatikan bahwa alamat tersebut harus disejajarkan dengan ukuran halaman memori (4096).

Register ASYNCLISTADDR memiliki offset + 0x18, Anda dapat memasukkan alamat antrian asinkron di dalamnya, perhatikan bahwa itu harus disejajarkan pada batas 32 byte, sementara itu harus dalam empat gigabytes pertama dari memori fisik.

Register CONFIGFLAG menunjukkan apakah perangkat dikonfigurasi. Anda harus menetapkan bit 0 setelah menyelesaikan pengaturan perangkat, ia memiliki offset + 0x40.

Mari kita beralih ke register port. Setiap port memiliki register status-perintahnya sendiri, setiap register port diimbangi + 0x44 + (PortNumber - 1) * 4 , bit-bitnya berarti sebagai berikut:

  • Bit 12 - port power, 1 - power dipasok, 0 - no.
  • Bit 8 - Port Rest - diatur untuk mengatur ulang perangkat.
  • Bit 3 - Port Enable / Disable Change - atur ketika mengubah status "inklusi" port.
  • Bit 2 - port on / off.
  • Bit 1 - Mengubah status koneksi, diatur ke 1, misalnya, jika Anda menghubungkan atau melepas perangkat USB.
  • Bit 0 - status koneksi, 1 - terhubung, 0 - no.

Sekarang mari kita beralih ke jus itu sendiri.

Transfer Data dan Struktur Permintaan


Mengatur struktur untuk memproses permintaan termasuk antrian dan transfer deskriptor (TDs).

Saat ini, kami hanya akan mempertimbangkan 3 struktur.

Daftar berurutan


Daftar berurutan (Periodik, Pereodik) disusun sebagai berikut:

gambar

Seperti yang Anda lihat dalam diagram, pemrosesan dimulai dengan memperoleh bingkai yang diinginkan dari bingkai sheet, masing-masing elemennya menempati 4 byte dan memiliki struktur sebagai berikut:

gambar

Seperti yang dapat Anda lihat dalam gambar, transfer antrian / descriptor diselaraskan pada batas 32 byte, bit 0 berarti bahwa pengontrol host tidak akan memproses elemen ini, bit 3: 1 menunjukkan jenis apa yang akan diproses oleh pengontrol host: 0 - isosynchronous TD (iTD), 1 - turn, 2 dan 3 dalam artikel ini saya tidak akan pertimbangkan.

Antrian asinkron


Pengontrol host memproses antrian ini hanya ketika frame berurutan kosong atau pengontrol host telah memproses seluruh daftar seri.

Antrian asinkron adalah penunjuk ke antrian yang berisi antrian lain yang perlu diproses. Skema:

gambar

qTD (Penjelasan Transfer Elemen Antrian)


TD ini memiliki struktur berikut:

gambar

Next qTD Pointer - pointer ke kelanjutan antrian untuk diproses (untuk Eksekusi Horizontal), bit 0 Next qTD Pointer menunjukkan bahwa tidak ada lagi antrian.
qTD Token - TD token, menunjukkan parameter transfer data:

  • Bit 31 - Data Toggle (lebih lanjut tentang itu nanti)
  • Bit 30:16 - jumlah data yang ditransfer, setelah penyelesaian transaksi, nilainya berkurang dengan jumlah data yang ditransfer.
  • Bit 15 - IOC - Interrupt On Complete - menyebabkan gangguan setelah pemrosesan deskriptor selesai.
  • Bit 14:12 menunjukkan jumlah buffer saat ini ke / dari mana data dipertukarkan, lebih lanjut tentang ini nanti.
  • Bit 11:10 - jumlah kesalahan yang diizinkan. Tabel ini menunjukkan kapan jumlah kesalahan berkurang:

    gambar

    Catatan Kaki 1 - mendeteksi Babble atau Stall secara otomatis menghentikan eksekusi kepala antrian. Catatan Kaki 3 - Kesalahan buffer data adalah masalah dengan host. Mereka tidak memperhitungkan coba ulang perangkat.
  • 9: 8 - Kode PID - jenis token: 0 - token ke input (dari host ke perangkat), 1 - token ke output (dari perangkat ke host), 2 - token “SETUP”
  • Bits 7: 0 menunjukkan status TD:
    Bit 7 menunjukkan bahwa TD dalam keadaan aktif (mis., Pengendali host memproses TD ini)
    Bit 6 - Dihentikan - menunjukkan bahwa kesalahan telah terjadi dan eksekusi TD telah berhenti.
    Bit 4 - Babble Detected - jumlah data yang kami kirim ke perangkat, atau per revolusi, kurang dari yang kami kirimkan, mis., Perangkat mengirimi kami 100 byte data, dan kami hanya membaca 50 byte, dan kemudian 50 lainnya, Bit yang Dihentikan juga akan diatur jika bit ini disetel ke 1.
    Bit 3 - Kesalahan Transaksi - Kesalahan terjadi selama transaksi.

qTD Buffer Page Pointer List - salah satu dari 5 buffer. Ini berisi tautan ke mana dalam memori transaksi harus dilakukan (mengirim data ke perangkat / menerima data dari perangkat), semua alamat dalam buffer, kecuali yang pertama, harus disejajarkan dengan ukuran halaman (4096 byte).

Kepala baris


Kepala Antrian memiliki struktur berikut:

gambar

Queue Head Horizontal Link Pointer - penunjuk ke antrian berikutnya, bit 2: 1 memiliki nilai berikut tergantung pada jenis antrian:

gambar

Kemampuan / Karakteristik Endpoint - karakteristik antrian:

  • Bit 26:16 berisi ukuran paket maksimum untuk transmisi
  • Bit 14: Data Toggle Control - menunjukkan di mana pengontrol host harus mengambil nilai Data Toggle awal, 0 - mengabaikan bit DT dalam qTD, menyimpan bit DT untuk kepala antrian.
  • Bit 13:12 - karakteristik laju transmisi: gambar
  • Bits 11: 8 - nomor titik akhir yang diminta permintaannya
  • Bit 6: 0 - alamat perangkat

Kemampuan Endpoint: Antrian Kepala DWord 2 - kelanjutan dari kata ganda sebelumnya:

  • Bit 29:23 - Nomor Hub
  • Bit 22:16 - Alamat hub

Current qTD Link Pointer - penunjuk ke qTD saat ini.

Kami lolos ke yang paling menarik.

Pengemudi EHCI


Mari kita mulai dengan pertanyaan apa yang bisa dipenuhi EHCI. Ada 2 jenis permintaan: Kontrol - perintah ala, dan Massal - ke titik akhir, untuk pertukaran data, misalnya, sebagian besar drive flash USB (USB MassStorage) menggunakan tipe transfer data Massal / Massal / Massal. Mouse dan keyboard juga menggunakan permintaan Massal untuk transfer data.

Inisialisasi EHCI dan konfigurasikan antrian asinkron dan berurutan:

// 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); 

Sebenarnya, kode untuk mengatur ulang port ke keadaan semula:

  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; 

Kontrol permintaan ke perangkat:

 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); } 

Kode Pemrosesan Antrian:

  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) .... 

Dan sekarang permintaan titik akhir (permintaan massal)

 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); } 

Saya pikir topiknya cukup menarik, di Internet dalam bahasa Rusia hampir tidak ada dokumentasi, deskripsi, dan artikel tentang topik ini, dan jika ada, itu sangat kabur. Jika topik bekerja dengan pengembangan perangkat keras dan OS menarik, maka ada banyak yang bisa diceritakan.

Dermaga: Spesifikasi

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


All Articles