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 m_DeviceAvailable(true) {
38 setSpecificType(String("usb-msd-controller"));
39}
40
41UsbMassStorageDevice::~UsbMassStorageDevice() {
42 if (m_DeviceAvailable) {
44 }
46}
47
49 {
50 LockGuard<Mutex> commandLock(m_CommandLock);
51 if (!m_DeviceAvailable) {
52 return;
53 }
54 m_DeviceAvailable = false;
55 }
56 shutdownDiskCaches(false);
58}
59
61 if (!m_pInterface || m_pInterface->nSubclass != 6 || m_pInterface->nProtocol != 0x50)
62 return;
63 for (size_t i = 0; i < m_pInterface->endpointList.count(); i++) {
64 Endpoint* pEndpoint = m_pInterface->endpointList[i];
65 if (!m_pInEndpoint && (pEndpoint->nTransferType == Endpoint::Bulk) && pEndpoint->bIn)
66 m_pInEndpoint = pEndpoint;
67 if (!m_pOutEndpoint && (pEndpoint->nTransferType == Endpoint::Bulk) && pEndpoint->bOut)
68 m_pOutEndpoint = pEndpoint;
69 if (m_pInEndpoint && m_pOutEndpoint)
70 break;
71 }
72
73 if (!m_pInEndpoint) {
74 ERROR("USB: MSD: No IN endpoint");
75 return;
76 }
77
78 if (!m_pOutEndpoint) {
79 ERROR("USB: MSD: No OUT endpoint");
80 return;
81 }
82
83 if (!massStorageReset())
84 return;
85 uint8_t maxLun = 0;
86 const ssize_t result = controlRequestResult(
87 static_cast<uint8_t>(UsbRequestDirection::In) | static_cast<uint8_t>(MassStorageRequest),
88 MassStorageGetMaxLUN, 0, m_pInterface->nInterface, 1, reinterpret_cast<uintptr_t>(&maxLun));
89 if (result == -Stall)
90 maxLun = 0;
91 else if (result != 1 || maxLun > 15) {
92 WARNING("USB: MSD: invalid GET_MAX_LUN response");
93 return;
94 }
95 m_nUnits = maxLun + 1;
96
97 searchDisks();
98
99 m_UsbState = HasDriver;
100}
101
102bool UsbMassStorageDevice::massStorageReset() {
103 return controlRequest(MassStorageRequest, MassStorageReset, 0, m_pInterface->nInterface);
104}
105
106bool UsbMassStorageDevice::performResetRecovery() {
107 m_ResetRecoveryRequired = true;
108 if (!m_pInterface || !m_pInEndpoint || !m_pOutEndpoint)
109 return false;
110
111 const bool reset = massStorageReset();
112 const bool clearedIn = clearEndpointHalt(m_pInEndpoint);
113 const bool clearedOut = clearEndpointHalt(m_pOutEndpoint);
114 if (reset && clearedIn && clearedOut)
115 m_ResetRecoveryRequired = false;
116 return !m_ResetRecoveryRequired;
117}
118
119UsbMassStorageDevice::BotStatus UsbMassStorageDevice::readStatus(uint32_t tag,
120 uint32_t expectedBytes) {
121 Csw* pCsw = new Csw;
122 PointerGuard<Csw> guard(pCsw);
123 ByteSet(pCsw, 0, sizeof(Csw));
124
125 ssize_t result = syncIn(m_pInEndpoint, reinterpret_cast<uintptr_t>(pCsw), sizeof(Csw));
126 if (result == -Stall) {
127 if (!clearEndpointHalt(m_pInEndpoint))
128 return BotStatus::RecoveryRequired;
129 ByteSet(pCsw, 0, sizeof(Csw));
130 result = syncIn(m_pInEndpoint, reinterpret_cast<uintptr_t>(pCsw), sizeof(Csw));
131 }
132
133 if (result != static_cast<ssize_t>(sizeof(Csw)) || pCsw->nSig != CswSig ||
134 pCsw->nTag != HOST_TO_LITTLE32(tag))
135 return BotStatus::RecoveryRequired;
136
137 const uint32_t residue = LITTLE_TO_HOST32(pCsw->nResidue);
138 if (residue > expectedBytes)
139 return BotStatus::RecoveryRequired;
140
141 if (pCsw->nStatus == 0)
142 return residue ? BotStatus::Failed : BotStatus::Passed;
143 if (pCsw->nStatus == 1)
144 return BotStatus::Failed;
145 return BotStatus::RecoveryRequired;
146}
147
148bool UsbMassStorageDevice::sendCommand(size_t nUnit, uintptr_t pCommand, uint8_t nCommandSize,
149 uintptr_t pRespBuffer, uint16_t nRespBytes, bool bWrite) {
150 if (!pCommand || !nCommandSize || nCommandSize > 16 || nUnit >= m_nUnits || nUnit > 0xf ||
151 (nRespBytes && !pRespBuffer) || !m_pInterface || !m_pInEndpoint || !m_pOutEndpoint)
152 return false;
153 LockGuard<Mutex> commandLock(m_CommandLock);
154 if (!m_DeviceAvailable) {
155 return false;
156 }
157 if (m_ResetRecoveryRequired && !performResetRecovery())
158 return false;
159
160 Cbw* pCbw = new Cbw;
161 PointerGuard<Cbw> guard(pCbw);
162 ByteSet(pCbw, 0, sizeof(Cbw));
163 const uint32_t tag = m_NextTag++;
164 pCbw->nSig = CbwSig;
165 pCbw->nTag = HOST_TO_LITTLE32(tag);
166 pCbw->nDataBytes = HOST_TO_LITTLE32(nRespBytes);
167 pCbw->nFlags = !bWrite && nRespBytes ? 0x80 : 0;
168 pCbw->nLUN = nUnit;
169 pCbw->nCommandSize = nCommandSize;
170 MemoryCopy(pCbw->pCommand, reinterpret_cast<void*>(pCommand), nCommandSize);
171
172 auto recover = [this]() {
173 if (!performResetRecovery())
174 WARNING("USB: MSD: reset recovery incomplete; new commands remain blocked");
175 return false;
176 };
177 if (syncOut(m_pOutEndpoint, reinterpret_cast<uintptr_t>(pCbw), sizeof(Cbw)) != sizeof(Cbw))
178 return recover();
179
180 ssize_t transferred = 0;
181 if (nRespBytes) {
182 transferred = bWrite ? syncOut(m_pOutEndpoint, pRespBuffer, nRespBytes)
183 : syncIn(m_pInEndpoint, pRespBuffer, nRespBytes);
184 if (transferred == -Stall) {
185 if (!clearEndpointHalt(bWrite ? m_pOutEndpoint : m_pInEndpoint))
186 return recover();
187 } else if (transferred < 0 || transferred > nRespBytes || (bWrite && transferred != nRespBytes))
188 return recover();
189 }
190 const BotStatus status = readStatus(tag, nRespBytes);
191 if (status == BotStatus::RecoveryRequired)
192 return recover();
193 // A valid CSW may complete an OUT STALL, but an IN failure never supplies
194 // bytes the controller did not receive. Short IN data must not become a
195 // successful SCSI cache fill even when the device reports zero residue.
196 return status == BotStatus::Passed && (bWrite || transferred == nRespBytes);
197}
virtual void destroy()
void shutdownDiskCaches(bool deviceAvailable=true)
bool clearEndpointHalt(Endpoint *pEndpoint)
Clears a halt on the given endpoint.
Definition UsbDevice.cc:573
Interface * m_pInterface
Interface in use.
Definition UsbDevice.h:320
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:558
UsbState m_UsbState
The current state of the device.
Definition UsbDevice.h:311
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:487
virtual void initialiseDriver()
Implemented by the driver class, initialises driver-specific stuff.
void prepareForDisconnection() override