Kernel/USB: Implement test transfer

We can now test a _very_ basic transaction via `do_debug_transfer()`.
This function merely attaches some TDs to the LSCTRL queue head
and points some input and output buffers. We then sense an interrupt
with USBSTS value of 1, meaning Interrupt On Completion
(of the transaction). At this point, the input buffer is filled with
some data.
This commit is contained in:
Jesse Buhagiar 2021-01-04 21:56:43 +11:00 committed by Andreas Kling
parent 3fb7e98e42
commit 6df15aca7e
Notes: sideshowbarker 2024-07-19 00:00:39 +09:00
4 changed files with 95 additions and 7 deletions

View file

@ -33,7 +33,8 @@
#include <Kernel/VM/MemoryManager.h>
#define UHCI_ENABLED 1
#define UHCI_DEBUG
//#define UHCI_DEBUG
//#define UHCI_VERBOSE_DEBUG
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;
@ -113,6 +114,8 @@ UHCIController::UHCIController(PCI::Address address, PCI::ID id)
reset();
start();
spawn_port_proc();
}
UHCIController::~UHCIController()
@ -136,6 +139,7 @@ void UHCIController::reset()
auto framelist_vmobj = ContiguousVMObject::create_with_size(PAGE_SIZE);
m_framelist = MemoryManager::the().allocate_kernel_region_with_vmobject(*framelist_vmobj, PAGE_SIZE, "UHCI Framelist", Region::Access::Write);
klog() << "UHCI: Allocated framelist at physical address " << m_framelist->physical_page(0)->paddr();
klog() << "FUCK!";
klog() << "UHCI: Framelist is at virtual address " << m_framelist->vaddr();
write_sofmod(64); // 1mS frame time
@ -193,7 +197,10 @@ void UHCIController::create_structures()
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->link_queue_head(m_interrupt_transfer_queue->paddr());
#ifdef UHCI_VERBOSE_DEBUG
transfer_descriptor->print();
#endif
}
kprintf("Done!\n");
@ -208,8 +215,11 @@ void UHCIController::create_structures()
// that we store in `paddr`, meaning our member functions directly
// access the raw descriptor (that we later send to the controller)
m_free_td_pool.at(i) = new (placement_addr) Kernel::USB::TransferDescriptor(paddr);
#ifdef UHCI_VERBOSE_DEBUG
auto transfer_descriptor = m_free_td_pool.at(i);
transfer_descriptor->print();
#endif
}
#ifdef UHCI_DEBUG
@ -332,29 +342,102 @@ void UHCIController::start()
klog() << "UHCI: Started!";
}
struct setup_packet {
u8 bmRequestType;
u8 bRequest;
u16 wValue;
u16 wIndex;
u16 wLength;
};
void UHCIController::do_debug_transfer()
{
klog() << "UHCI: Attempting a dummy transfer...";
// Okay, let's set up the buffer so we can write some data
auto vmobj = ContiguousVMObject::create_with_size(PAGE_SIZE);
m_td_buffer_region = MemoryManager::the().allocate_kernel_region_with_vmobject(*vmobj, PAGE_SIZE, "UHCI Debug Data Region", Region::Access::Write);
// We need to set up THREE Transfer descriptors here
// 1. The SETUP packet TD
// 2. The DATA packet
// 3. The ACK TD that will be filled by the device
// We can use the buffer pool provided above to do this, using nasty pointer offsets!
auto setup_td = allocate_transfer_descriptor();
auto data_td = allocate_transfer_descriptor();
auto response_td = allocate_transfer_descriptor();
kprintf("BUFFER PHYSICAL ADDRESS = 0x%08x\n", m_td_buffer_region->physical_page(0)->paddr().get());
setup_packet* packet = reinterpret_cast<setup_packet*>(m_td_buffer_region->vaddr().as_ptr());
packet->bmRequestType = 0x81;
packet->bRequest = 0x06;
packet->wValue = 0x2200;
packet->wIndex = 0x0;
packet->wLength = 8;
// Let's begin....
setup_td->set_status(0x18800000);
setup_td->set_token(0x00E0002D);
setup_td->set_buffer_address(m_td_buffer_region->physical_page(0)->paddr().get());
data_td->set_status(0x18800000);
data_td->set_token(0x00E80069);
data_td->set_buffer_address(m_td_buffer_region->physical_page(0)->paddr().get() + 16);
response_td->set_status(0x19800000);
response_td->set_token(0xFFE800E1);
setup_td->insert_next_transfer_descriptor(data_td);
data_td->insert_next_transfer_descriptor(response_td);
response_td->terminate();
setup_td->print();
data_td->print();
response_td->print();
// Now let's (attempt) to attach to one of the queue heads
m_lowspeed_control_qh->attach_transfer_descriptor_chain(setup_td);
}
void UHCIController::spawn_port_proc()
{
RefPtr<Thread> usb_hotplug_thread;
timespec sleep_time;
sleep_time.tv_sec = 1;
Process::create_kernel_process(usb_hotplug_thread, "UHCIHotplug", [sleep_time] {
Process::create_kernel_process(usb_hotplug_thread, "UHCIHotplug", [&, sleep_time] {
for (;;) {
for (int port = 0; port < UHCI_ROOT_PORT_COUNT; port++) {
u16 port_data = 0;
if (port == 1) {
// Let's see what's happening on port 1
port_data = UHCIController::the().read_portsc1();
// Current status
port_data = read_portsc1();
if (port_data & UHCI_PORTSC_CONNECT_STATUS_CHANGED) {
if (port_data & UHCI_PORTSC_CURRRENT_CONNECT_STATUS) {
klog() << "UHCI: Device attach detected on Root Port 1!";
// Reset the port
port_data = read_portsc1();
write_portsc1(port_data | UHCI_PORTSC_PORT_RESET);
for (size_t i = 0; i < 50000; ++i)
IO::in8(0x80);
write_portsc1(port_data & ~UHCI_PORTSC_PORT_RESET);
for (size_t i = 0; i < 100000; ++i)
IO::in8(0x80);
write_portsc1(port_data & (~UHCI_PORTSC_PORT_ENABLE_CHANGED | ~UHCI_PORTSC_CONNECT_STATUS_CHANGED));
} else {
klog() << "UHCI: Device detach detected on Root Port 1!";
}
UHCIController::the().write_portsc1(
UHCI_PORTSC_CONNECT_STATUS_CHANGED);
port_data = read_portsc1();
write_portsc1(port_data | UHCI_PORTSC_PORT_ENABLED);
kprintf("port should be enabled now: 0x%x\n", read_portsc1());
do_debug_transfer();
}
} else {
port_data = UHCIController::the().read_portsc2();
@ -378,11 +461,13 @@ void UHCIController::spawn_port_proc()
void UHCIController::handle_irq(const RegisterState&)
{
// Shared IRQ. Not ours!
if(!read_usbsts())
if (!read_usbsts())
return;
#ifdef UHCI_DEBUG
klog() << "UHCI: Interrupt happened!";
klog() << "Value of USBSTS: " << read_usbsts();
#endif
}
}

View file

@ -49,6 +49,8 @@ public:
void start();
void spawn_port_proc();
void do_debug_transfer();
private:
UHCIController(PCI::Address, PCI::ID);

View file

@ -247,7 +247,6 @@ void init_stage2(void*)
}
USB::UHCIController::detect();
USB::UHCIController::the().spawn_port_proc();
E1000NetworkAdapter::detect();
RTL8139NetworkAdapter::detect();

View file

@ -153,6 +153,8 @@ add_compile_definitions("THREAD_DEBUG")
add_compile_definitions("TLS_DEBUG")
add_compile_definitions("TTY_DEBUG")
add_compile_definitions("UCI_DEBUG")
add_compile_definitions("UHCI_DEBUG")
add_compile_definitions("UHCI_VERBOSE_DEBUG")
add_compile_definitions("UDP_DEBUG")
add_compile_definitions("UPDATE_COALESCING_DEBUG")
add_compile_definitions("VERY_DEBUG")