20#include "UsbMassStorageDevice.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/utilities/PointerGuard.h"
24#include "pedigree/kernel/utilities/Vector.h"
26#include "modules/system/usb/Usb.h"
27#include "modules/system/usb/UsbDevice.h"
29UsbMassStorageDevice::UsbMassStorageDevice(
UsbDevice* dev)
36 m_ResetRecoveryRequired(false) {
37 setSpecificType(
String(
"usb-msd-controller"));
40UsbMassStorageDevice::~UsbMassStorageDevice() {
48 for (
size_t i = 0; i <
m_pInterface->endpointList.count(); i++) {
50 if (!m_pInEndpoint && (pEndpoint->nTransferType == Endpoint::Bulk) && pEndpoint->bIn)
51 m_pInEndpoint = pEndpoint;
52 if (!m_pOutEndpoint && (pEndpoint->nTransferType == Endpoint::Bulk) && pEndpoint->bOut)
53 m_pOutEndpoint = pEndpoint;
54 if (m_pInEndpoint && m_pOutEndpoint)
59 ERROR(
"USB: MSD: No IN endpoint");
63 if (!m_pOutEndpoint) {
64 ERROR(
"USB: MSD: No OUT endpoint");
68 if (!massStorageReset())
72 static_cast<uint8_t
>(UsbRequestDirection::In) |
static_cast<uint8_t
>(MassStorageRequest),
73 MassStorageGetMaxLUN, 0,
m_pInterface->nInterface, 1,
reinterpret_cast<uintptr_t
>(&maxLun));
76 else if (result != 1 || maxLun > 15) {
77 WARNING(
"USB: MSD: invalid GET_MAX_LUN response");
80 m_nUnits = maxLun + 1;
87bool UsbMassStorageDevice::massStorageReset() {
91bool UsbMassStorageDevice::performResetRecovery() {
92 m_ResetRecoveryRequired =
true;
96 const bool reset = massStorageReset();
99 if (reset && clearedIn && clearedOut)
100 m_ResetRecoveryRequired =
false;
101 return !m_ResetRecoveryRequired;
104UsbMassStorageDevice::BotStatus UsbMassStorageDevice::readStatus(uint32_t tag,
105 uint32_t expectedBytes) {
108 ByteSet(pCsw, 0,
sizeof(Csw));
110 ssize_t result = syncIn(m_pInEndpoint,
reinterpret_cast<uintptr_t
>(pCsw),
sizeof(Csw));
111 if (result == -Stall) {
113 return BotStatus::RecoveryRequired;
114 ByteSet(pCsw, 0,
sizeof(Csw));
115 result = syncIn(m_pInEndpoint,
reinterpret_cast<uintptr_t
>(pCsw),
sizeof(Csw));
118 if (result !=
static_cast<ssize_t
>(
sizeof(Csw)) || pCsw->nSig != CswSig ||
119 pCsw->nTag != HOST_TO_LITTLE32(tag))
120 return BotStatus::RecoveryRequired;
122 const uint32_t residue = LITTLE_TO_HOST32(pCsw->nResidue);
123 if (residue > expectedBytes)
124 return BotStatus::RecoveryRequired;
126 if (pCsw->nStatus == 0)
127 return residue ? BotStatus::Failed : BotStatus::Passed;
128 if (pCsw->nStatus == 1)
129 return BotStatus::Failed;
130 return BotStatus::RecoveryRequired;
133bool UsbMassStorageDevice::sendCommand(
size_t nUnit, uintptr_t pCommand, uint8_t nCommandSize,
134 uintptr_t pRespBuffer, uint16_t nRespBytes,
bool bWrite) {
135 if (!pCommand || !nCommandSize || nCommandSize > 16 || nUnit >= m_nUnits || nUnit > 0xf ||
136 (nRespBytes && !pRespBuffer) || !
m_pInterface || !m_pInEndpoint || !m_pOutEndpoint)
139 if (m_ResetRecoveryRequired && !performResetRecovery())
144 ByteSet(pCbw, 0,
sizeof(Cbw));
145 const uint32_t tag = m_NextTag++;
147 pCbw->nTag = HOST_TO_LITTLE32(tag);
148 pCbw->nDataBytes = HOST_TO_LITTLE32(nRespBytes);
149 pCbw->nFlags = !bWrite && nRespBytes ? 0x80 : 0;
151 pCbw->nCommandSize = nCommandSize;
152 MemoryCopy(pCbw->pCommand,
reinterpret_cast<void*
>(pCommand), nCommandSize);
154 auto recover = [
this]() {
155 if (!performResetRecovery())
156 WARNING(
"USB: MSD: reset recovery incomplete; new commands remain blocked");
159 if (syncOut(m_pOutEndpoint,
reinterpret_cast<uintptr_t
>(pCbw),
sizeof(Cbw)) !=
sizeof(Cbw))
162 ssize_t transferred = 0;
164 transferred = bWrite ? syncOut(m_pOutEndpoint, pRespBuffer, nRespBytes)
165 : syncIn(m_pInEndpoint, pRespBuffer, nRespBytes);
166 if (transferred == -Stall) {
169 }
else if (transferred < 0 || transferred > nRespBytes || (bWrite && transferred != nRespBytes))
172 const BotStatus status = readStatus(tag, nRespBytes);
173 if (status == BotStatus::RecoveryRequired)
178 return status == BotStatus::Passed && (bWrite || transferred == nRespBytes);
void shutdownDiskCaches()
bool clearEndpointHalt(Endpoint *pEndpoint)
Clears a halt on the given endpoint.
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.
ssize_t controlRequestResult(uint8_t nRequestType, uint8_t nRequest, uint16_t nValue, uint16_t nIndex, uint16_t nLength=0, uintptr_t pBuffer=0, uint32_t timeout=5000)
Returns data bytes transferred, or a negative UsbError.
virtual void initialiseDriver()
Implemented by the driver class, initialises driver-specific stuff.