20#include "UsbHubDevice.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/machine/Machine.h"
23#include "pedigree/kernel/panic.h"
24#include "pedigree/kernel/time/Time.h"
26#include "modules/system/usb/UsbDevice.h"
27#include "modules/system/usb/UsbHub.h"
30constexpr size_t PortResetPollLimit = 100;
31constexpr uint8_t HubDescriptorType = 0x29;
32constexpr size_t PortDebouncePollLimit = 10;
41 m_StatusBuffer(nullptr),
45 attachToUpstreamHub(m_pHub, {m_nRootPort, m_nRootPortGeneration});
48UsbHubDevice::~UsbHubDevice() {
52 delete[] m_StatusBuffer;
68void UsbHubDevice::stopHotplug() {
70 if (!m_StatusInterrupt.
reset()) {
71 panic(
"USB hub destroyed from a running interrupt callback");
88 pDesc =
getDescriptor(HubDescriptorType, 0, len, UsbRequestType::Class);
97 DEBUG_LOG(
"USB: HUB: Found a hub with "
98 <<
Dec << pDescriptor.nPorts <<
Hex
99 <<
" ports and hubCharacteristics = " << pDescriptor.hubCharacteristics);
100 m_nPorts = pDescriptor.nPorts;
105 const size_t powerGoodDelay =
static_cast<size_t>(pDescriptor.powerGoodDelay) * 2;
106 if (powerGoodDelay > m_PowerGoodDelay) {
107 m_PowerGoodDelay = powerGoodDelay;
109 for (
size_t i = 0; i < m_nPorts; i++) {
111 uint32_t portStatus = 0;
113 WARNING(
"USB: HUB: couldn't read port " <<
Dec << i <<
Hex);
119 if (!(portStatus & (1 << 8))) {
120 DEBUG_LOG(
"USB: HUB: Powering up port " <<
Dec << i <<
Hex <<
" [status = " << portStatus
124 if (!setPortFeature(i, PortPower)) {
125 WARNING(
"USB: HUB: couldn't power port " <<
Dec << i <<
Hex);
131 if (!Time::delay(m_PowerGoodDelay * Time::Multiplier::Millisecond)) {
143 if (!(portStatus & (1 << 8))) {
144 DEBUG_LOG(
"USB: HUB: Port " <<
Dec << i <<
Hex <<
" couldn't be powered up.");
149 DEBUG_LOG(
"USB: HUB: Powered up port " <<
Dec << i <<
Hex <<
" [status = " << portStatus
153 if (!clearPortChanges(i, portStatus)) {
157 if ((portStatus & (1 << PortConnection)) && !connectPort(i)) {
163 for (
size_t i = 0; i <
m_pInterface->endpointList.count(); ++i) {
165 if (endpoint->bIn && endpoint->nTransferType == Endpoint::Interrupt) {
166 statusEndpoint = endpoint;
170 m_StatusBytes = (m_nPorts + 8) / 8;
171 if (!statusEndpoint || statusEndpoint->nMaxPacketSize < m_StatusBytes) {
172 ERROR(
"USB: HUB: no usable status-change endpoint");
176 m_StatusBuffer =
new uint8_t[m_StatusBytes]();
177 if (!m_StatusChange.configure(*
this, 0)) {
183 !UsbDevice::addInterruptInHandler(statusEndpoint,
reinterpret_cast<uintptr_t
>(m_StatusBuffer),
184 m_StatusBytes, statusChanged, m_StatusInterrupt,
185 reinterpret_cast<uintptr_t
>(
this))) {
186 ERROR(
"USB: HUB: status-change submission failed");
193 NOTICE(
"USB: HUB: hotplug ready, address " <<
Dec <<
m_nAddress <<
", ports " << m_nPorts <<
Hex);
196void UsbHubDevice::statusChanged(uintptr_t parameter, ssize_t result) {
198 if (result <= 0 ||
static_cast<size_t>(result) > hub->m_StatusBytes) {
201 bool changed =
false;
202 for (
size_t i = 0; i < static_cast<size_t>(result); ++i) {
203 changed = changed || hub->m_StatusBuffer[i];
211 const auto observation = hub->m_StatusChange.observe();
212 if (UsbHcd::PortChangeRequest::canAcknowledge(observation.result)) {
213 hub->m_StatusChange.acknowledge(observation.generation);
218 uint64_t, uint64_t generation) {
224 uint32_t hubStatus = 0;
225 if (
controlRequest(
static_cast<uint8_t
>(UsbRequestDirection::In) | HubRequest,
226 UsbRequest::GetStatus, 0, 0,
sizeof(hubStatus),
227 reinterpret_cast<uintptr_t
>(&hubStatus))) {
228 for (
size_t feature = HubLocalPower; feature <= HubOverCurrent; ++feature) {
229 if (hubStatus & (1U << (feature + 16))) {
230 if (!
controlRequest(HubRequest, UsbRequest::ClearFeature, feature, 0)) {
231 WARNING(
"USB: HUB: couldn't clear hub change " <<
Dec << feature <<
Hex);
236 for (
size_t port = 0; port < m_nPorts; ++port) {
246 m_StatusChange.cancel(request.p8);
249bool UsbHubDevice::clearPortChanges(
size_t port, uint32_t status) {
251 for (
size_t feature = CPortConnection; feature <= CPortReset; ++feature) {
252 if (status & (1U << feature)) {
253 if (!clearPortFeature(port,
static_cast<PortFeatureSelectors
>(feature))) {
261bool UsbHubDevice::debouncePort(
size_t port, uint32_t& status) {
262 for (
size_t poll = 0; poll < PortDebouncePollLimit; ++poll) {
264 if (state == RequestQueue::LifecycleState::Stopping ||
265 state == RequestQueue::LifecycleState::Destroyed) {
268 if (!clearPortFeature(port, CPortConnection) ||
269 !Time::delay(100 * Time::Multiplier::Millisecond) || !
getPortStatus(port, status)) {
272 if (!(status & (1 << PortConnection))) {
275 if (!(status & (1U << CPortConnection))) {
279 WARNING(
"USB: HUB: connection on port " <<
Dec << port <<
Hex <<
" did not settle");
283bool UsbHubDevice::connectPort(
size_t port) {
285 if (!debouncePort(port, status)) {
296 if (status & (1 << 10)) {
298 }
else if (status & (1 << PortLowSpeed)) {
306void UsbHubDevice::portChanged(
size_t port) {
311 if (!clearPortChanges(port, status)) {
312 WARNING(
"USB: HUB: couldn't clear changes on port " <<
Dec << port <<
Hex);
316 const bool connectionChanged = status & (1U << CPortConnection);
317 const bool disabled = (status & (1U << CPortEnable)) && !(status & (1 << PortEnable));
318 const bool overCurrentChanged = status & (1U << CPortOverCurrent);
319 if (!connectionChanged && !disabled && !overCurrentChanged) {
324 if (status & (1 << PortOverCurrent)) {
327 if (!(status & (1 << PortPower))) {
328 if (!setPortFeature(port, PortPower) ||
329 !Time::delay(m_PowerGoodDelay * Time::Multiplier::Millisecond)) {
336 if (!(status & (1 << PortConnection))) {
339 if (!connectPort(port)) {
340 WARNING(
"USB: HUB: couldn't enumerate changed port " <<
Dec << port <<
Hex);
345 (void)bErrorResponse;
346 if (nPort >= m_nPorts)
350 if (!setPortFeature(nPort, PortReset))
354 if (!Time::delay(50 * Time::Multiplier::Millisecond))
358 uint32_t portStatus = 0;
360 for (; poll < PortResetPollLimit; ++poll) {
362 if (state == RequestQueue::LifecycleState::Stopping ||
363 state == RequestQueue::LifecycleState::Destroyed) {
368 if (!(portStatus & (1 << 4)))
370 if (!Time::delay(Time::Multiplier::Millisecond))
373 if (poll == PortResetPollLimit) {
374 ERROR(
"USB: HUB: reset on port " <<
Dec <<
static_cast<size_t>(nPort) <<
Hex <<
" timed out");
377 if (!clearPortFeature(nPort, CPortReset))
382 return ((portStatus & 0x3) == 0x3);
385bool UsbHubDevice::setPortFeature(
size_t port, PortFeatureSelectors feature) {
386 return controlRequest(HubPortRequest, UsbRequest::SetFeature, feature, (port + 1) & 0xFF, 0, 0);
389bool UsbHubDevice::clearPortFeature(
size_t port, PortFeatureSelectors feature) {
390 return controlRequest(HubPortRequest, UsbRequest::ClearFeature, feature, (port + 1) & 0xFF, 0, 0);
395 return controlRequest(
static_cast<uint8_t
>(
static_cast<uint8_t
>(UsbRequestDirection::In) |
396 static_cast<uint8_t
>(HubPortRequest)),
397 UsbRequest::GetStatus, 0, (port + 1) & 0xFF,
sizeof(status),
398 reinterpret_cast<uintptr_t
>(&status));
402 uintptr_t pBuffer,
size_t nBytes) {
407 if (endpointInfo.speed != HighSpeed && !endpointInfo.nHubAddress) {
410 ++endpointInfo.nHubPort;
412 endpointInfo.nHubPort =
m_nPort;
423 void (*pCallback)(uintptr_t, ssize_t), uintptr_t pParam) {
428 uint16_t nBytes,
void (*pCallback)(uintptr_t, ssize_t),
430 if (endpointInfo.speed != HighSpeed && !endpointInfo.nHubAddress) {
433 ++endpointInfo.nHubPort;
435 endpointInfo.nHubPort =
m_nPort;
442 void (*callback)(uintptr_t, ssize_t),
443 uintptr_t parameter,
bool producerAlreadyStopped) {
447 (void)producerAlreadyStopped;
448 panic(
"downstream USB hub unexpectedly owned an interrupt-IN handle");
static void setShutdownDetail(const char *detail)
LifecycleState getLifecycleState()
virtual void initialise()
UsbHub * m_pHub
Parent USB hub.
uint8_t m_nAddress
The current address of the device.
Interface * m_pInterface
Interface in use.
bool controlRequest(uint8_t nRequestType, uint8_t nRequest, uint16_t nValue, uint16_t nIndex, uint16_t nLength=0, uintptr_t pBuffer=0, uint32_t timeout=5000)
Performs an USB control request.
UsbState m_UsbState
The current state of the device.
UsbSpeed m_Speed
The speed at which the device operates.
uint8_t m_nPort
The number of the port on which the device is connected.
uint8_t getDescriptorLength(uint8_t nDescriptorType, uint8_t nDescriptorIndex, uint8_t requestType=0)
Gets a descriptor's length from the device.
DeviceDescriptor * getDescriptor()
Returns the device descriptor of the device.
bool portSpeed(uint8_t port, UsbSpeed &speed) override
void prepareForDisconnection() override
bool getPortStatus(size_t port, uint32_t &status)
Top 16 bits of status hold the port-change flags.
virtual void cancelAsyncAndDrain(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t), uintptr_t pParam)
MUST_USE_RESULT bool cancelInterruptInAndDrain(const UsbInterruptInToken &token, void(*callback)(uintptr_t, ssize_t), uintptr_t parameter, bool producerAlreadyStopped) override
void quiesceForRetirement() override
void prepareForDriverRetirement() override
virtual MUST_USE_RESULT bool doAsync(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t)=0, uintptr_t pParam=0)
virtual bool portReset(uint8_t nPort, bool bErrorResponse=false)
Gets a UsbDevice from a given vendor:product pair.
virtual void addTransferToTransaction(uintptr_t pTransaction, bool bToggle, UsbPid pid, uintptr_t pBuffer, size_t nBytes)
Adds a new transfer to an existent transaction.
void cancelRequest(const Request &request) override
virtual void initialiseDriver()
Implemented by the driver class, initialises driver-specific stuff.
uint64_t executeRequest(uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t p5, uint64_t p6, uint64_t p7, uint64_t p8) override
virtual MUST_USE_RESULT bool addInterruptInHandler(UsbEndpoint endpointInfo, uintptr_t pBuffer, uint16_t nBytes, void(*pCallback)(uintptr_t, ssize_t), UsbInterruptInHandle &handle, uintptr_t pParam=0)
Adds an owned recurring interrupt-IN transaction.
virtual uintptr_t createTransaction(UsbEndpoint endpointInfo)
Creates a new transaction with the given endpoint data.
virtual void addTransferToTransaction(uintptr_t pTransaction, bool bToggle, UsbPid pid, uintptr_t pBuffer, size_t nBytes)=0
Adds a new transfer to an existent transaction.
virtual uintptr_t createTransaction(UsbEndpoint endpointInfo)=0
Creates a new transaction with the given endpoint data.
void retainDisconnectedAddressesUntilControllerTeardown()
virtual void cancelAsyncAndDrain(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t), uintptr_t pParam)=0
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.
virtual MUST_USE_RESULT bool addInterruptInHandler(UsbEndpoint endpointInfo, uintptr_t pBuffer, uint16_t nBytes, void(*pCallback)(uintptr_t, ssize_t), UsbInterruptInHandle &handle, uintptr_t pParam=0)=0
Adds an owned recurring interrupt-IN transaction.
virtual MUST_USE_RESULT bool doAsync(uintptr_t pTransaction, void(*pCallback)(uintptr_t, ssize_t)=0, uintptr_t pParam=0)=0
void disconnectAllDevices()
MUST_USE_RESULT bool reset()
void EXPORTED_PUBLIC panic(const char *msg) NORETURN