The Pedigree Project 0.1
UsbHubDevice.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "UsbHubDevice.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/panic.h"
23#include "pedigree/kernel/time/Time.h"
24
25#include "modules/system/usb/UsbDevice.h"
26#include "modules/system/usb/UsbHub.h"
27
28namespace {
29constexpr size_t PortResetPollLimit = 100;
30}
31
32UsbHubDevice::UsbHubDevice(UsbDevice* dev) : UsbDevice(dev), UsbHub() {
33 attachToUpstreamHub(m_pHub, {m_nRootPort, m_nRootPortGeneration});
34}
35
36UsbHubDevice::~UsbHubDevice() {
38}
39
43
45 StartupActivity startup(this);
46 if (!startup)
47 return;
48 uint8_t len = getDescriptorLength(0, 0, UsbRequestType::Class);
49 void* pDesc = 0;
50 if (len) {
51 pDesc = getDescriptor(0, 0, len, UsbRequestType::Class);
52 if (!pDesc)
53 return;
54 } else
55 return;
56
57 HubDescriptor pDescriptor(pDesc);
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++) {
63 // Grab this port's status
64 uint32_t portStatus = 0;
65 if (!getPortStatus(i, portStatus)) {
66 WARNING("USB: HUB: couldn't read port " << Dec << i << Hex);
67 startup.failed();
68 continue;
69 }
70
71 // Is power on?
72 if (!(portStatus & (1 << 8))) {
73 DEBUG_LOG("USB: HUB: Powering up port " << Dec << i << Hex << " [status = " << portStatus
74 << "]...");
75
76 // Power it on
77 if (!setPortFeature(i, PortPower)) {
78 WARNING("USB: HUB: couldn't power port " << Dec << i << Hex);
79 startup.failed();
80 continue;
81 }
82
83 // Delay while the power goes on
84 if (!Time::delay(50 * Time::Multiplier::Millisecond)) {
85 startup.failed();
86 continue;
87 }
88
89 // Done.
90 if (!getPortStatus(i, portStatus)) {
91 startup.failed();
92 continue;
93 }
94
95 // If port power never went on, skip this port
96 if (!(portStatus & (1 << 8))) {
97 DEBUG_LOG("USB: HUB: Port " << Dec << i << Hex << " couldn't be powered up.");
98 startup.failed();
99 continue;
100 }
101
102 DEBUG_LOG("USB: HUB: Powered up port " << Dec << i << Hex << " [status = " << portStatus
103 << "]...");
104 }
105
106 if (portReset(i)) {
107 // Got a device - what type?
108 if (!getPortStatus(i, portStatus)) {
109 startup.failed();
110 continue;
111 }
112 if (portStatus & (1 << 10)) {
113 // High-speed
114 DEBUG_LOG("USB: HUB: Hub port " << Dec << i << Hex
115 << " has a high-speed device attached to it.");
116 deviceConnected(i, HighSpeed);
117 } else if (portStatus & (1 << 9)) {
118 // Low-speed
119 DEBUG_LOG("USB: HUB: Hub port " << Dec << i << Hex
120 << " has a low-speed device attached to it.");
121 deviceConnected(i, LowSpeed);
122 } else {
123 // Full-speed
124 DEBUG_LOG("USB: HUB: Hub port " << Dec << i << Hex
125 << " has a full-speed device attached to it.");
126 deviceConnected(i, FullSpeed);
127 }
128 } else if (portStatus & 1) {
129 startup.failed();
130 }
131 }
132
133 m_UsbState = HasDriver;
134}
135
136bool UsbHubDevice::portReset(uint8_t nPort, bool bErrorResponse) {
137 (void)bErrorResponse;
138 if (nPort >= m_nPorts)
139 return false;
140
141 // Reset the port
142 if (!setPortFeature(nPort, PortReset))
143 return false;
144
145 // Delay while the reset completes
146 if (!Time::delay(50 * Time::Multiplier::Millisecond))
147 return false;
148
149 // Wait for completion
150 uint32_t portStatus = 0;
151 size_t poll = 0;
152 for (; poll < PortResetPollLimit; ++poll) {
153 if (!getPortStatus(nPort, portStatus))
154 return false;
155 if (!(portStatus & (1 << 4)))
156 break;
157 if (!Time::delay(Time::Multiplier::Millisecond))
158 return false;
159 }
160 if (poll == PortResetPollLimit) {
161 ERROR("USB: HUB: reset on port " << Dec << static_cast<size_t>(nPort) << Hex << " timed out");
162 return false;
163 }
164 if (!clearPortFeature(nPort, CPortReset))
165 return false;
166
167 // Port has been powered on and now reset, check to see if it's enabled and
168 // a device is connected
169 return ((portStatus & 0x3) == 0x3);
170}
171
172bool UsbHubDevice::setPortFeature(size_t port, PortFeatureSelectors feature) {
173 return controlRequest(HubPortRequest, UsbRequest::SetFeature, feature, (port + 1) & 0xFF, 0, 0);
174}
175
176bool UsbHubDevice::clearPortFeature(size_t port, PortFeatureSelectors feature) {
177 return controlRequest(HubPortRequest, UsbRequest::ClearFeature, feature, (port + 1) & 0xFF, 0, 0);
178}
179
180bool UsbHubDevice::getPortStatus(size_t port, uint32_t& status) {
181 status = 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));
186}
187
188void UsbHubDevice::addTransferToTransaction(uintptr_t pTransaction, bool bToggle, UsbPid pid,
189 uintptr_t pBuffer, size_t nBytes) {
190 m_pHub->addTransferToTransaction(pTransaction, bToggle, pid, pBuffer, nBytes);
191}
192
194 if (endpointInfo.speed != HighSpeed && !endpointInfo.nHubAddress) {
195 if (m_Speed == HighSpeed) {
196 endpointInfo.nHubAddress = m_nAddress;
197 ++endpointInfo.nHubPort;
198 } else
199 endpointInfo.nHubPort = m_nPort;
200 }
201 return m_pHub->createTransaction(endpointInfo);
202}
203
204bool UsbHubDevice::doAsync(uintptr_t pTransaction, void (*pCallback)(uintptr_t, ssize_t),
205 uintptr_t pParam) {
206 return m_pHub->doAsync(pTransaction, pCallback, pParam);
207}
208
209void UsbHubDevice::cancelAsyncAndDrain(uintptr_t pTransaction,
210 void (*pCallback)(uintptr_t, ssize_t), uintptr_t pParam) {
211 m_pHub->cancelAsyncAndDrain(pTransaction, pCallback, pParam);
212}
213
214bool UsbHubDevice::addInterruptInHandler(UsbEndpoint endpointInfo, uintptr_t pBuffer,
215 uint16_t nBytes, void (*pCallback)(uintptr_t, ssize_t),
216 UsbInterruptInHandle& handle, uintptr_t pParam) {
217 if (endpointInfo.speed != HighSpeed && !endpointInfo.nHubAddress) {
218 if (m_Speed == HighSpeed) {
219 endpointInfo.nHubAddress = m_nAddress;
220 ++endpointInfo.nHubPort;
221 } else
222 endpointInfo.nHubPort = m_nPort;
223 }
224 // The upstream call publishes the handle directly against the root HCD.
225 return m_pHub->addInterruptInHandler(endpointInfo, pBuffer, nBytes, pCallback, handle, pParam);
226}
227
229 void (*callback)(uintptr_t, ssize_t),
230 uintptr_t parameter, bool producerAlreadyStopped) {
231 (void)token;
232 (void)callback;
233 (void)parameter;
234 (void)producerAlreadyStopped;
235 panic("downstream USB hub unexpectedly owned an interrupt-IN handle");
236 return false;
237}
UsbHub * m_pHub
Parent USB hub.
Definition UsbDevice.h:317
uint8_t m_nAddress
The current address of the device.
Definition UsbDevice.h:291
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.
Definition UsbDevice.cc:556
UsbState m_UsbState
The current state of the device.
Definition UsbDevice.h:305
UsbSpeed m_Speed
The speed at which the device operates.
Definition UsbDevice.h:302
uint8_t m_nPort
The number of the port on which the device is connected.
Definition UsbDevice.h:295
uint8_t getDescriptorLength(uint8_t nDescriptorType, uint8_t nDescriptorIndex, uint8_t requestType=0)
Gets a descriptor's length from the device.
Definition UsbDevice.cc:641
DeviceDescriptor * getDescriptor()
Returns the device descriptor of the device.
Definition UsbDevice.h:216
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()
Definition UsbHub.cc:241
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.
Definition UsbHub.cc:387
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()
Definition UsbHub.cc:216
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:117
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124