20#include "UsbHubDevice.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/panic.h"
23#include "pedigree/kernel/time/Time.h"
25#include "modules/system/usb/UsbDevice.h"
26#include "modules/system/usb/UsbHub.h"
29constexpr size_t PortResetPollLimit = 100;
33 attachToUpstreamHub(m_pHub, {m_nRootPort, m_nRootPortGeneration});
36UsbHubDevice::~UsbHubDevice() {
58 DEBUG_LOG(
"USB: HUB: Found a hub with "
59 <<
Dec << pDescriptor.nPorts <<
Hex
60 <<
" ports and hubCharacteristics = " << pDescriptor.hubCharacteristics);
61 m_nPorts = pDescriptor.nPorts;
62 for (
size_t i = 0; i < m_nPorts; i++) {
64 uint32_t portStatus = 0;
66 WARNING(
"USB: HUB: couldn't read port " <<
Dec << i <<
Hex);
72 if (!(portStatus & (1 << 8))) {
73 DEBUG_LOG(
"USB: HUB: Powering up port " <<
Dec << i <<
Hex <<
" [status = " << portStatus
77 if (!setPortFeature(i, PortPower)) {
78 WARNING(
"USB: HUB: couldn't power port " <<
Dec << i <<
Hex);
84 if (!Time::delay(50 * Time::Multiplier::Millisecond)) {
96 if (!(portStatus & (1 << 8))) {
97 DEBUG_LOG(
"USB: HUB: Port " <<
Dec << i <<
Hex <<
" couldn't be powered up.");
102 DEBUG_LOG(
"USB: HUB: Powered up port " <<
Dec << i <<
Hex <<
" [status = " << portStatus
112 if (portStatus & (1 << 10)) {
114 DEBUG_LOG(
"USB: HUB: Hub port " <<
Dec << i <<
Hex
115 <<
" has a high-speed device attached to it.");
117 }
else if (portStatus & (1 << 9)) {
119 DEBUG_LOG(
"USB: HUB: Hub port " <<
Dec << i <<
Hex
120 <<
" has a low-speed device attached to it.");
124 DEBUG_LOG(
"USB: HUB: Hub port " <<
Dec << i <<
Hex
125 <<
" has a full-speed device attached to it.");
128 }
else if (portStatus & 1) {
137 (void)bErrorResponse;
138 if (nPort >= m_nPorts)
142 if (!setPortFeature(nPort, PortReset))
146 if (!Time::delay(50 * Time::Multiplier::Millisecond))
150 uint32_t portStatus = 0;
152 for (; poll < PortResetPollLimit; ++poll) {
155 if (!(portStatus & (1 << 4)))
157 if (!Time::delay(Time::Multiplier::Millisecond))
160 if (poll == PortResetPollLimit) {
161 ERROR(
"USB: HUB: reset on port " <<
Dec <<
static_cast<size_t>(nPort) <<
Hex <<
" timed out");
164 if (!clearPortFeature(nPort, CPortReset))
169 return ((portStatus & 0x3) == 0x3);
172bool UsbHubDevice::setPortFeature(
size_t port, PortFeatureSelectors feature) {
173 return controlRequest(HubPortRequest, UsbRequest::SetFeature, feature, (port + 1) & 0xFF, 0, 0);
176bool UsbHubDevice::clearPortFeature(
size_t port, PortFeatureSelectors feature) {
177 return controlRequest(HubPortRequest, UsbRequest::ClearFeature, feature, (port + 1) & 0xFF, 0, 0);
182 return controlRequest(
static_cast<uint8_t
>(
static_cast<uint8_t
>(UsbRequestDirection::In) |
183 static_cast<uint8_t
>(HubPortRequest)),
184 UsbRequest::GetStatus, 0, (port + 1) & 0xFF,
sizeof(status),
185 reinterpret_cast<uintptr_t
>(&status));
189 uintptr_t pBuffer,
size_t nBytes) {
194 if (endpointInfo.speed != HighSpeed && !endpointInfo.nHubAddress) {
197 ++endpointInfo.nHubPort;
199 endpointInfo.nHubPort =
m_nPort;
210 void (*pCallback)(uintptr_t, ssize_t), uintptr_t pParam) {
215 uint16_t nBytes,
void (*pCallback)(uintptr_t, ssize_t),
217 if (endpointInfo.speed != HighSpeed && !endpointInfo.nHubAddress) {
220 ++endpointInfo.nHubPort;
222 endpointInfo.nHubPort =
m_nPort;
229 void (*callback)(uintptr_t, ssize_t),
230 uintptr_t parameter,
bool producerAlreadyStopped) {
234 (void)producerAlreadyStopped;
235 panic(
"downstream USB hub unexpectedly owned an interrupt-IN handle");
UsbHub * m_pHub
Parent USB hub.
uint8_t m_nAddress
The current address of the device.
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 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 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.
virtual void initialiseDriver()
Implemented by the driver class, initialises driver-specific stuff.
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.
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()
void EXPORTED_PUBLIC panic(const char *msg) NORETURN