2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/Log.h"
4#include "pedigree/kernel/time/Time.h"
5#include "pedigree/kernel/utilities/utility.h"
9bool Xhci::collectEventsLocked(
bool fromInterrupt) {
10 const bool credit = fromInterrupt && m_PolledInterrupt;
12 m_PolledInterrupt =
false;
15 const uint32_t status = read(m_Op + 4);
16 const uint32_t iman = read(m_Runtime + 0x20);
17 const bool cause = (status & 0x1cU) || (iman & 1U);
18 if (status & ((1U << 12) | 4U)) {
23 write(m_Op + 4, status & 0x18U);
25 write(m_Runtime + 0x20, 3);
27 bool consumed =
false;
28 for (
size_t count = 0; count < RingEntries; ++count) {
29 volatile auto& source = entries[m_EventHead];
30 const uint32_t control = source.control;
31 if (
bool(control & Cycle) != m_EventCycle)
34 Trb event{source.parameter, source.status, control};
35 if (++m_EventHead == RingEntries) {
37 m_EventCycle = !m_EventCycle;
38 if (!m_EventWraps++) {
39 NOTICE(
"xHCI: event ring wrapped");
40#if PEDIGREE_USB_SMOKE_TESTS
41 NOTICE(
"XHCI-SMOKE: event-ring-wrap");
46 const uint32_t type = (control >> 10) & 63U;
48 if (!m_CommandAddress || event.parameter != m_CommandAddress) {
54 m_CommandCode =
event.status >> 24;
55 m_CommandSlot = control >> 24;
57 }
else if (type == 32) {
58 const bool valid = transferEventLocked(event);
59#if PEDIGREE_USB_SMOKE_TESTS
60 if (valid && fromInterrupt && m_Online && !m_ObservedInterruptCompletion) {
61 m_ObservedInterruptCompletion =
true;
62 NOTICE(
"XHCI-SMOKE: interrupt-completion");
67 }
else if (type == 34) {
68 const size_t port = (
event.parameter >> 24) & 255U;
69 if (!port || port > m_PortCount) {
73 const size_t reg = m_Op + 0x400 + (port - 1) * 16;
74 const uint32_t portStatus = read(reg);
75 m_Ports[port - 1].resetChanges |= portStatus & ((1U << 19) | (1U << 21));
76 if (portStatus & (1U << 17))
77 m_Ports[port - 1].changePending =
true;
78 write(reg, (portStatus & 0x0e00c200U) | (portStatus & PortChanges));
80 if ((portStatus & (1U << 17)) && !m_PortsClosing)
81 notifyPortLocked(port - 1);
82 }
else if (type == 37) {
89 if (m_Online && (consumed || cause)) {
91 m_PolledInterrupt =
true;
94 (void)read(m_Runtime + 0x38);
96 return cause || consumed || credit;
102 return collectEventsLocked(
true) ? IrqDisposition::Handled : IrqDisposition::NotHandled;
104void Xhci::notifyPortLocked(
size_t port) {
107 m_Ports[port].changePending =
true;
110 const auto observation = m_PortChanges[port].observe();
111 if (!UsbHcd::PortChangeRequest::canAcknowledge(observation.result)) {
112 ERROR(
"xHCI: root-port publication failed");
116 m_PortChanges[port].acknowledge(observation.generation);
117 m_Ports[port].changePending =
false;
120 return port < m_PortCount ? m_PortChanges[port].observedGeneration() : 0;
124 if (port < m_PortCount && !m_PortsClosing)
125 notifyPortLocked(port);
128 if (port >= m_PortCount || !m_Online)
130 const size_t reg = m_Op + 0x400 + port * 16;
131 const bool warm = errorResponse && m_Ports[port].major == 3;
132 const uint32_t completion = warm ? 1U << 19 : 1U << 21;
135 const uint32_t status = read(reg);
138 m_Ports[port].resetChanges = 0;
139 write(reg, (status & 0x0e00c200U) | (status & completion) | (warm ? 1U << 31 : 1U << 4));
142 const auto deadline = Time::getTicks() + Time::Multiplier::Second;
146 const uint32_t ready = read(reg);
147 if (!m_Online || !(ready & 1U))
150 if (!(ready & (1U << 4)) && ((ready | m_Ports[port].resetChanges) & completion)) {
151 const uint8_t speed = (ready >> 10) & 15U;
153 write(reg, (ready & 0x0e00c200U) | (ready & ((1U << 19) | (1U << 21))));
155 return (ready & 3U) == 3U && m_Ports[port].validSpeed[speed];
158 Time::delay(Time::Multiplier::Millisecond);
159 }
while (Time::getTicks() < deadline);
163 uint64_t, uint64_t generation) {
164 if (port >= m_PortCount)
167 if (!completion || m_PortsClosing || !m_Online)
169 uint32_t status = read(m_Op + 0x400 + port * 16);
174 if (!m_Ports[port].validSpeed[(status >> 10) & 15U]) {
177 status = read(m_Op + 0x400 + port * 16);
179 const uint8_t speed = (status >> 10) & 15U;
180 if (m_Ports[port].validSpeed[speed])
183 WARNING(
"xHCI: unsupported root-port speed ID " << speed);
188 if (request.p1 < m_PortCount)
189 m_PortChanges[request.p1].cancel(request.p8);
void * virtualAddress() const
physical_uintptr_t physicalAddress() const
bool deviceConnected(uint8_t nPort, UsbSpeed speed)
Called when a device is connected to a port on the hub.
void deviceDisconnected(uint8_t nPort)
Called when a device is disconnected from a port on the hub.
MUST_USE_RESULT bool deferConnectionChangeIfSuppressed(size_t port)
IrqDisposition irq(irq_id_t) override
void replaySuppressedConnectionChange(size_t port) override
bool portReset(uint8_t port, bool errorResponse=false) override
Gets a UsbDevice from a given vendor:product pair.
uint64_t executeRequest(uint64_t port, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t generation) override
void cancelRequest(const Request &request) override
size_t currentRootPortGeneration(size_t port) const override