The Pedigree Project 0.1
UsbMassStorageDevice.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 "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"
25
26#include "modules/system/usb/Usb.h"
27#include "modules/system/usb/UsbDevice.h"
28
29UsbMassStorageDevice::UsbMassStorageDevice(UsbDevice* dev)
31 UsbDevice(dev),
32 m_nUnits(0),
33 m_pInEndpoint(0),
34 m_pOutEndpoint(0),
35 m_NextTag(1),
36 m_ResetRecoveryRequired(false) {
37 setSpecificType(String("usb-msd-controller"));
38}
39
40UsbMassStorageDevice::~UsbMassStorageDevice() {
43}
44
46 if (!m_pInterface || m_pInterface->nSubclass != 6 || m_pInterface->nProtocol != 0x50)
47 return;
48 for (size_t i = 0; i < m_pInterface->endpointList.count(); i++) {
49 Endpoint* pEndpoint = m_pInterface->endpointList[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)
55 break;
56 }
57
58 if (!m_pInEndpoint) {
59 ERROR("USB: MSD: No IN endpoint");
60 return;
61 }
62
63 if (!m_pOutEndpoint) {
64 ERROR("USB: MSD: No OUT endpoint");
65 return;
66 }
67
68 if (!massStorageReset())
69 return;
70 uint8_t maxLun = 0;
71 const ssize_t result = controlRequestResult(
72 static_cast<uint8_t>(UsbRequestDirection::In) | static_cast<uint8_t>(MassStorageRequest),
73 MassStorageGetMaxLUN, 0, m_pInterface->nInterface, 1, reinterpret_cast<uintptr_t>(&maxLun));
74 if (result == -Stall)
75 maxLun = 0;
76 else if (result != 1 || maxLun > 15) {
77 WARNING("USB: MSD: invalid GET_MAX_LUN response");
78 return;
79 }
80 m_nUnits = maxLun + 1;
81
82 searchDisks();
83
84 m_UsbState = HasDriver;
85}
86
87bool UsbMassStorageDevice::massStorageReset() {
88 return controlRequest(MassStorageRequest, MassStorageReset, 0, m_pInterface->nInterface);
89}
90
91bool UsbMassStorageDevice::performResetRecovery() {
92 m_ResetRecoveryRequired = true;
93 if (!m_pInterface || !m_pInEndpoint || !m_pOutEndpoint)
94 return false;
95
96 const bool reset = massStorageReset();
97 const bool clearedIn = clearEndpointHalt(m_pInEndpoint);
98 const bool clearedOut = clearEndpointHalt(m_pOutEndpoint);
99 if (reset && clearedIn && clearedOut)
100 m_ResetRecoveryRequired = false;
101 return !m_ResetRecoveryRequired;
102}
103
104UsbMassStorageDevice::BotStatus UsbMassStorageDevice::readStatus(uint32_t tag,
105 uint32_t expectedBytes) {
106 Csw* pCsw = new Csw;
107 PointerGuard<Csw> guard(pCsw);
108 ByteSet(pCsw, 0, sizeof(Csw));
109
110 ssize_t result = syncIn(m_pInEndpoint, reinterpret_cast<uintptr_t>(pCsw), sizeof(Csw));
111 if (result == -Stall) {
112 if (!clearEndpointHalt(m_pInEndpoint))
113 return BotStatus::RecoveryRequired;
114 ByteSet(pCsw, 0, sizeof(Csw));
115 result = syncIn(m_pInEndpoint, reinterpret_cast<uintptr_t>(pCsw), sizeof(Csw));
116 }
117
118 if (result != static_cast<ssize_t>(sizeof(Csw)) || pCsw->nSig != CswSig ||
119 pCsw->nTag != HOST_TO_LITTLE32(tag))
120 return BotStatus::RecoveryRequired;
121
122 const uint32_t residue = LITTLE_TO_HOST32(pCsw->nResidue);
123 if (residue > expectedBytes)
124 return BotStatus::RecoveryRequired;
125
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;
131}
132
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)
137 return false;
138 LockGuard<Mutex> commandLock(m_CommandLock);
139 if (m_ResetRecoveryRequired && !performResetRecovery())
140 return false;
141
142 Cbw* pCbw = new Cbw;
143 PointerGuard<Cbw> guard(pCbw);
144 ByteSet(pCbw, 0, sizeof(Cbw));
145 const uint32_t tag = m_NextTag++;
146 pCbw->nSig = CbwSig;
147 pCbw->nTag = HOST_TO_LITTLE32(tag);
148 pCbw->nDataBytes = HOST_TO_LITTLE32(nRespBytes);
149 pCbw->nFlags = !bWrite && nRespBytes ? 0x80 : 0;
150 pCbw->nLUN = nUnit;
151 pCbw->nCommandSize = nCommandSize;
152 MemoryCopy(pCbw->pCommand, reinterpret_cast<void*>(pCommand), nCommandSize);
153
154 auto recover = [this]() {
155 if (!performResetRecovery())
156 WARNING("USB: MSD: reset recovery incomplete; new commands remain blocked");
157 return false;
158 };
159 if (syncOut(m_pOutEndpoint, reinterpret_cast<uintptr_t>(pCbw), sizeof(Cbw)) != sizeof(Cbw))
160 return recover();
161
162 ssize_t transferred = 0;
163 if (nRespBytes) {
164 transferred = bWrite ? syncOut(m_pOutEndpoint, pRespBuffer, nRespBytes)
165 : syncIn(m_pInEndpoint, pRespBuffer, nRespBytes);
166 if (transferred == -Stall) {
167 if (!clearEndpointHalt(bWrite ? m_pOutEndpoint : m_pInEndpoint))
168 return recover();
169 } else if (transferred < 0 || transferred > nRespBytes || (bWrite && transferred != nRespBytes))
170 return recover();
171 }
172 const BotStatus status = readStatus(tag, nRespBytes);
173 if (status == BotStatus::RecoveryRequired)
174 return recover();
175 // A valid CSW may complete an OUT STALL, but an IN failure never supplies
176 // bytes the controller did not receive. Short IN data must not become a
177 // successful SCSI cache fill even when the device reports zero residue.
178 return status == BotStatus::Passed && (bWrite || transferred == nRespBytes);
179}
virtual void destroy()
void shutdownDiskCaches()
bool clearEndpointHalt(Endpoint *pEndpoint)
Clears a halt on the given endpoint.
Definition UsbDevice.cc:571
Interface * m_pInterface
Interface in use.
Definition UsbDevice.h:314
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
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.
Definition UsbDevice.cc:485
virtual void initialiseDriver()
Implemented by the driver class, initialises driver-specific stuff.