UHCI, atau USB pertama



Selamat siang, pembaca yang budiman! Saya diminta untuk menulis tentang UHCI - well, saya menulis.

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 UHCI?


Saya pikir, agar tidak menyemprot lagi pada topik apa dan mengapa, tinggalkan saja tautan ke artikel saya sebelumnya tentang EHCI. Aduk di sini
UHCI - Universal Host Controller Interface, beroperasi sebagai perangkat PCI, tetapi, tidak seperti EHCI menggunakan port, bukan MMIO (Memory-Mapped-IO).



Ketentuan untuk digunakan selanjutnya


  • Driver USB (USBD) - driver USB itu sendiri
  • HC (Host Controller) - pengendali host, atau hanya UHCI kami
  • Host Controller Driver (HCD) - driver yang menghubungkan perangkat keras dan USBD
  • Perangkat USB - perangkat USB itu sendiri

Jenis-jenis Transfer Data


Isochronous - transmisi isosynchronous, yang memiliki frekuensi transfer data tertentu. Ini dapat digunakan, misalnya, untuk mikrofon USB, dll.

Interrupt - Kecil, transfer data spontan dari suatu perangkat. Jenis transmisi interupsi mendukung perangkat yang memerlukan interval layanan yang dapat diprediksi tetapi tidak selalu menyediakan aliran data yang dapat diprediksi. Biasa digunakan untuk perangkat seperti papan ketik dan perangkat penunjuk yang mungkin tidak menyediakan data untuk jangka waktu yang lama, tetapi memerlukan respons cepat ketika mereka memiliki data untuk dikirim.

Kontrol - Jenis pengiriman informasi tentang status perangkat, status dan konfigurasi. Jenis transfer Kontrol digunakan untuk menyediakan saluran kontrol dari Host ke perangkat USB. Transmisi kontrol selalu terdiri dari fase pengaturan dan nol atau lebih fase data diikuti oleh fase status. Sangat penting bahwa kontrol transfer ke titik akhir yang diberikan diproses dalam mode FIFO. Jika kontrol dilewatkan ke titik akhir yang sama, interleaving dapat menyebabkan perilaku yang tidak terduga.

Jenis transfer array data secara massal. Digunakan, misalnya, di perangkat MassStorage.



Ini adalah bagaimana distribusi waktu 1ms terlihat seperti - pemrosesan satu frame.

Distribusi waktu


Pengontrol host mendukung pengiriman data real-time dengan menghasilkan paket Start Of Frame (SOF) setiap 1 ms. Paket SOF dihasilkan ketika penghitung SOF di pengontrol host kedaluwarsa (Gambar 3). Pengontrol host menginisialisasi penghitung SOF untuk waktu bingkai 1 ms. Perubahan kecil dapat dibuat untuk nilai ini (dan karenanya periode waktu bingkai) dengan memprogram register perubahan SOF. Fitur ini memungkinkan Anda untuk membuat perubahan kecil pada periode waktu bingkai, jika perlu, untuk mempertahankan sinkronisasi waktu nyata di seluruh sistem USB.

Pengontrol host menyertakan nomor bingkai di setiap paket SOF. Nomor bingkai ini secara unik menentukan periode bingkai secara real time. Kondisi akhir bingkai (EOF) terjadi pada akhir interval waktu 1 ms ketika pengendali host memulai waktu bingkai berikutnya, menghasilkan paket SOF lain dengan nomor bingkai yang sesuai. Selama periode frame, data ditransmisikan sebagai paket informasi. Periode waktu frame diberlakukan secara ketat oleh pengontrol host, dan paket data dalam frame saat ini tidak dapat melampaui EOF (lihat Bab 11 dalam spesifikasi USB). Pengendali host mendukung sinkronisasi pengiriman data antara frame secara real time, menghubungkan nomor frame untuk melakukan entri tertentu dalam daftar frame. Penghitung bingkai pengontrol host menghasilkan nomor bingkai (nilai 11-bit) dan memasukkannya ke dalam setiap paket SOF. Penghitung diprogram melalui register dan setiap periode frame bertambah. Pengontrol host menggunakan 10 bit lebih rendah dari jumlah frame sebagai indeks dalam daftar frame dengan 1024 frame, yang disimpan dalam memori sistem. Jadi, karena penghitung bingkai mengontrol pemilihan entri dari daftar bingkai, pengontrol host memproses setiap entri dalam daftar dalam periode bingkai yang diberikan. Pengontrol host memperluas ke entri berikutnya dalam daftar bingkai untuk setiap bingkai baru. Ini memastikan bahwa transmisi isochronous dilakukan dalam bingkai tertentu.

Gambar 3:



Struktur UHCI


Semuanya persis sama dengan EHCI. Contoh permintaan ke HC:



Konfigurasikan dan akses UHCI


Jadi, seperti yang saya katakan sebelumnya, UHCI bekerja melalui port, jadi dari PCI kita perlu mencari tahu basis register UHCI.



Pada offset 0x20 ada 4 byte - IO Base. Mengenai Basis IO, kita dapat menggunakan register berikut:



Register UHCI


  • USBCMD adalah register untuk mengendalikan HC. Bit:
    • Bit 6 adalah bendera yang berhasil dikonfigurasikan dan diinisialisasi oleh perangkat.
    • Bit 1 - Reset HC. Setel untuk mengatur ulang HC.
    • Bit 0 - Run / Stop. Menampilkan status HC. 1 - bekerja, 0 - tidak.
  • USBSTS - Status Register. Bit:
    • Bit 5 - HC Dihentikan. Terjadi kesalahan, atau pengontrol berhasil menyelesaikan HC Reset.
    • Bit 4 - Kesalahan Proses Pengontrol Host. Bit diatur ke 1 ketika kesalahan kritis telah terjadi dan HC tidak dapat melanjutkan antrian dan TD.
    • Bit 3 - Kesalahan Sistem Host. Kesalahan PCI.
    • Bit 1 - Interupsi Kesalahan. Menunjukkan bahwa telah terjadi kesalahan dan HC menghasilkan interupsi.
    • Bit 0 - Interrupt. Menunjukkan bahwa HC menghasilkan interupsi.
  • USBINTR - Daftar pengaturan interupsi. Bit:
    • Bit 2 - IOC - Interrupt on complete - menghasilkan interrupt pada akhir transaksi.
  • FRNUM - Jumlah bingkai saat ini (Ambil & 0x3FF untuk nilai yang benar).
  • FLBASEADD - Frame List Base Address - alamat dari daftar frame.
  • PORTSC - Status port dan kontrol - status dan register kontrol port. Bit:
    • Bit 9 - Port Reset - 1 - port untuk mengatur ulang.
    • Bit 8 - menunjukkan bahwa perangkat berkecepatan rendah terhubung ke port
    • Bit 3 - Menunjukkan bahwa port on state telah berubah
    • Bit 2 - Menunjukkan apakah port diaktifkan
    • Bit 1 - menunjukkan bahwa status perangkat terhubung ke port
    • Bit 0 - menunjukkan bahwa perangkat terhubung ke port.

Struktur


Pointer daftar penunjuk




Descrptor transfer




KONTROL DAN STATUS TD
. Bit:
  • Bit 28-27 - penghitung kesalahan, mirip dengan EHCI.
    • Bit 26 - 1 = Perangkat berkecepatan rendah, 0 = Perangkat berkecepatan penuh.
    • Bit 25 - 1 = TD sinkron
    • Bit 24 - IOC
    • Bit 23-16 - Status:
    • Bit 23 - Menunjukkan bahwa itu adalah TD aktif
    • Bit 22 - Stalled
    • Bit 21 - Kesalahan Penyangga Data
    • Bit 20 - Babble Terdeteksi
    • Bit 19 - NAK
  • Bit 10-0: Jumlah byte yang dikirimkan oleh pengontrol host.

Token TD

  • Bits 31:21 - Max Packet Len, mirip dengan EHCI
  • Bit 19 - Data Toggle, Mirip dengan EHCI
  • Bit 18:15 - Nomor Titik Akhir
  • Bit 18:14 - alamat perangkat
  • Bits 7: 0 - PID. Dalam = 0x69, Keluar = 0xE1, Pengaturan = 0x2D

Kepala antrian




Kode


Inisialisasi dan konfigurasi 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); 

Permintaan titik akhir dan kontrol:

 // ------------------------------------------------------------------------------------------------ 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/id429422/


All Articles