Kernel/USB: Rework UHCI interrupt transfer schedule

This reworks the way the UHCI schedule is set up to handle interrupt
transfers, creating 11 queue heads each assigned a different
period/latency, so that interrupt transfers can be linked into the
schedule with their specified period more easily.
This commit is contained in:
b14ckcat 2022-07-17 18:53:18 -04:00 committed by Andreas Kling
parent 4a3a0ac19e
commit 550b3c7330
Notes: sideshowbarker 2024-07-17 11:33:34 +09:00
2 changed files with 28 additions and 11 deletions

View File

@ -132,8 +132,12 @@ UNMAP_AFTER_INIT ErrorOr<void> UHCIController::create_structures()
// Used as a sentinel value to loop back to the beginning of the list // Used as a sentinel value to loop back to the beginning of the list
m_schedule_begin_anchor = allocate_queue_head(); m_schedule_begin_anchor = allocate_queue_head();
// Each interrupt QH anchor in the array is linked into the schedule so that
// it is executed once every (2^i) milliseconds, where i is it's index
for (int i = 0; i < NUMBER_OF_INTERRUPT_QHS; i++) {
m_interrupt_qh_anchor_arr[i] = allocate_queue_head();
}
// Create the Full Speed, Low Speed Control and Bulk Queue Heads // Create the Full Speed, Low Speed Control and Bulk Queue Heads
m_interrupt_qh_anchor = allocate_queue_head();
m_ls_control_qh_anchor = allocate_queue_head(); m_ls_control_qh_anchor = allocate_queue_head();
m_fs_control_qh_anchor = allocate_queue_head(); m_fs_control_qh_anchor = allocate_queue_head();
m_bulk_qh_anchor = allocate_queue_head(); m_bulk_qh_anchor = allocate_queue_head();
@ -157,7 +161,6 @@ UNMAP_AFTER_INIT ErrorOr<void> UHCIController::create_structures()
auto transfer_descriptor = m_iso_td_list.at(i); auto transfer_descriptor = m_iso_td_list.at(i);
transfer_descriptor->set_in_use(true); // Isochronous transfers are ALWAYS marked as in use (in case we somehow get allocated one...) transfer_descriptor->set_in_use(true); // Isochronous transfers are ALWAYS marked as in use (in case we somehow get allocated one...)
transfer_descriptor->set_isochronous(); transfer_descriptor->set_isochronous();
transfer_descriptor->link_queue_head(m_interrupt_qh_anchor->paddr());
if constexpr (UHCI_VERBOSE_DEBUG) if constexpr (UHCI_VERBOSE_DEBUG)
transfer_descriptor->print(); transfer_descriptor->print();
@ -196,11 +199,15 @@ UNMAP_AFTER_INIT void UHCIController::setup_schedule()
// Not specified in the datasheet, however, is another Queue Head with an "inactive" Transfer Descriptor. This // Not specified in the datasheet, however, is another Queue Head with an "inactive" Transfer Descriptor. This
// is to circumvent a bug in the silicon of the PIIX4's UHCI controller. // is to circumvent a bug in the silicon of the PIIX4's UHCI controller.
// https://github.com/openbsd/src/blob/master/sys/dev/usb/uhci.c#L390 // https://github.com/openbsd/src/blob/master/sys/dev/usb/uhci.c#L390
m_schedule_begin_anchor->link_next_queue_head(m_interrupt_qh_anchor); m_schedule_begin_anchor->link_next_queue_head(m_interrupt_qh_anchor_arr[0]);
m_schedule_begin_anchor->terminate_element_link_ptr(); m_schedule_begin_anchor->terminate_element_link_ptr();
m_interrupt_qh_anchor->link_next_queue_head(m_ls_control_qh_anchor); for (int i = 0; i < NUMBER_OF_INTERRUPT_QHS - 1; i++) {
m_interrupt_qh_anchor->terminate_element_link_ptr(); m_interrupt_qh_anchor_arr[i]->link_next_queue_head(m_interrupt_qh_anchor_arr[i + 1]);
m_interrupt_qh_anchor_arr[i]->terminate_element_link_ptr();
}
m_interrupt_qh_anchor_arr[NUMBER_OF_INTERRUPT_QHS - 1]->link_next_queue_head(m_ls_control_qh_anchor);
m_interrupt_qh_anchor_arr[NUMBER_OF_INTERRUPT_QHS - 1]->terminate_element_link_ptr();
m_ls_control_qh_anchor->link_next_queue_head(m_fs_control_qh_anchor); m_ls_control_qh_anchor->link_next_queue_head(m_fs_control_qh_anchor);
m_ls_control_qh_anchor->terminate_element_link_ptr(); m_ls_control_qh_anchor->terminate_element_link_ptr();
@ -217,12 +224,21 @@ UNMAP_AFTER_INIT void UHCIController::setup_schedule()
m_bulk_qh_anchor->attach_transfer_descriptor_chain(piix4_td_hack); m_bulk_qh_anchor->attach_transfer_descriptor_chain(piix4_td_hack);
u32* framelist = reinterpret_cast<u32*>(m_framelist->vaddr().as_ptr()); u32* framelist = reinterpret_cast<u32*>(m_framelist->vaddr().as_ptr());
for (int frame = 0; frame < UHCI_NUMBER_OF_FRAMES; frame++) { for (int frame_num = 0; frame_num < UHCI_NUMBER_OF_FRAMES; frame_num++) {
auto frame_iso_td = m_iso_td_list.at(frame_num % UHCI_NUMBER_OF_ISOCHRONOUS_TDS);
// Each frame pointer points to iso_td % NUM_ISO_TDS // Each frame pointer points to iso_td % NUM_ISO_TDS
framelist[frame] = m_iso_td_list.at(frame % UHCI_NUMBER_OF_ISOCHRONOUS_TDS)->paddr(); for (int i = NUMBER_OF_INTERRUPT_QHS - 1; i >= 0; i--) {
if (frame_num % (1 << i) == 0) {
frame_iso_td->link_queue_head(m_interrupt_qh_anchor_arr[i]->paddr());
break;
}
}
framelist[frame_num] = frame_iso_td->paddr();
} }
m_interrupt_qh_anchor->print(); for (int i = 0; i < NUMBER_OF_INTERRUPT_QHS; i++) {
m_interrupt_qh_anchor_arr[i]->print();
}
m_ls_control_qh_anchor->print(); m_ls_control_qh_anchor->print();
m_fs_control_qh_anchor->print(); m_fs_control_qh_anchor->print();
m_bulk_qh_anchor->print(); m_bulk_qh_anchor->print();

View File

@ -7,9 +7,9 @@
#pragma once #pragma once
#include <AK/Platform.h> #include <AK/Array.h>
#include <AK/NonnullOwnPtr.h> #include <AK/NonnullOwnPtr.h>
#include <AK/Platform.h>
#include <Kernel/Arch/x86/IO.h> #include <Kernel/Arch/x86/IO.h>
#include <Kernel/Bus/PCI/Device.h> #include <Kernel/Bus/PCI/Device.h>
#include <Kernel/Bus/USB/UHCI/UHCIDescriptorPool.h> #include <Kernel/Bus/USB/UHCI/UHCIDescriptorPool.h>
@ -31,6 +31,7 @@ class UHCIController final
static constexpr u8 MAXIMUM_NUMBER_OF_TDS = 128; // Upper pool limit. This consumes the second page we have allocated static constexpr u8 MAXIMUM_NUMBER_OF_TDS = 128; // Upper pool limit. This consumes the second page we have allocated
static constexpr u8 MAXIMUM_NUMBER_OF_QHS = 64; static constexpr u8 MAXIMUM_NUMBER_OF_QHS = 64;
static constexpr u8 NUMBER_OF_INTERRUPT_QHS = 11;
public: public:
static constexpr u8 NUMBER_OF_ROOT_PORTS = 2; static constexpr u8 NUMBER_OF_ROOT_PORTS = 2;
@ -102,7 +103,7 @@ private:
Vector<TransferDescriptor*> m_iso_td_list; Vector<TransferDescriptor*> m_iso_td_list;
QueueHead* m_schedule_begin_anchor; QueueHead* m_schedule_begin_anchor;
QueueHead* m_interrupt_qh_anchor; Array<QueueHead*, NUMBER_OF_INTERRUPT_QHS> m_interrupt_qh_anchor_arr;
QueueHead* m_ls_control_qh_anchor; QueueHead* m_ls_control_qh_anchor;
QueueHead* m_fs_control_qh_anchor; QueueHead* m_fs_control_qh_anchor;
// Always final queue in the schedule, may loop back to previous QH for bandwidth // Always final queue in the schedule, may loop back to previous QH for bandwidth