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 "modules/system/usb/Usb.h"
22 #include "modules/system/usb/UsbDevice.h"
23 #include "pedigree/kernel/Log.h"
24 #include "pedigree/kernel/utilities/PointerGuard.h"
25 #include "pedigree/kernel/utilities/Vector.h"
26 
27 UsbMassStorageDevice::UsbMassStorageDevice(UsbDevice *dev)
28  : ScsiController(), UsbDevice(dev), m_nUnits(0), m_pInEndpoint(0),
29  m_pOutEndpoint(0)
30 {
31 }
32 
33 UsbMassStorageDevice::~UsbMassStorageDevice()
34 {
35 }
36 
38 {
39  for (size_t i = 0; i < m_pInterface->endpointList.count(); i++)
40  {
41  Endpoint *pEndpoint = m_pInterface->endpointList[i];
42  if (!m_pInEndpoint && (pEndpoint->nTransferType == Endpoint::Bulk) &&
43  pEndpoint->bIn)
44  m_pInEndpoint = pEndpoint;
45  if (!m_pOutEndpoint && (pEndpoint->nTransferType == Endpoint::Bulk) &&
46  pEndpoint->bOut)
47  m_pOutEndpoint = pEndpoint;
48  if (m_pInEndpoint && m_pOutEndpoint)
49  break;
50  }
51 
52  if (!m_pInEndpoint)
53  {
54  ERROR("USB: MSD: No IN endpoint");
55  return;
56  }
57 
58  if (!m_pOutEndpoint)
59  {
60  ERROR("USB: MSD: No OUT endpoint");
61  return;
62  }
63 
64  // Reset the mass storage device and associated interface
65  massStorageReset();
66 
67  // Get the maximum LUN and find out the number of units
71  uint8_t *nMaxLUN = new uint8_t(0);
72  if (!controlRequest(
73  UsbRequestDirection::In | MassStorageRequest, MassStorageGetMaxLUN,
74  0, m_pInterface->nInterface, 1,
75  reinterpret_cast<uintptr_t>(nMaxLUN)))
76  {
77  ERROR("USB: MSD: Couldn't get maximum LUN");
78  return;
79  }
80  m_nUnits = *nMaxLUN + 1;
81  delete nMaxLUN;
82 
83  searchDisks();
84 
85  m_UsbState = HasDriver;
86 }
87 
88 bool UsbMassStorageDevice::massStorageReset()
89 {
90  return controlRequest(
91  MassStorageRequest, MassStorageReset, 0, m_pInterface->nInterface);
92 }
93 
95  size_t nUnit, uintptr_t pCommand, uint8_t nCommandSize,
96  uintptr_t pRespBuffer, uint16_t nRespBytes, bool bWrite)
97 {
98  Cbw *pCbw = new Cbw;
99  PointerGuard<Cbw> guard(pCbw);
100  ByteSet(pCbw, 0, sizeof(Cbw));
101  pCbw->nSig = CbwSig;
102  pCbw->nDataBytes = HOST_TO_LITTLE32(nRespBytes);
103  pCbw->nFlags = bWrite ? 0 : 0x80;
104  pCbw->nLUN = nUnit;
105  pCbw->nCommandSize = nCommandSize;
106  MemoryCopy(
107  pCbw->pCommand, reinterpret_cast<void *>(pCommand), nCommandSize);
108 
109  ssize_t nResult =
110  syncOut(m_pOutEndpoint, reinterpret_cast<uintptr_t>(pCbw), 31);
111 
112  // Handle stall
113  if (nResult == -Stall)
114  {
115  // Clear out pipe
116  if (!clearEndpointHalt(m_pOutEndpoint))
117  {
118  // Reset and fail this command
119  massStorageReset();
121  clearEndpointHalt(m_pOutEndpoint);
122  return false;
123  }
124  else
125  nResult = 0; // Attempt data transfer
126  }
127 
128  if (nResult < 0)
129  return false;
130 
131  // Handle data or CSW transfer if needed
132  if (nRespBytes)
133  {
134  DEBUG_LOG(
135  "USB: MSD: Performing " << Dec << nRespBytes << Hex << " byte "
136  << (bWrite ? "write" : "read"));
137  if (bWrite)
138  nResult = syncOut(m_pOutEndpoint, pRespBuffer, nRespBytes);
139  else
140  nResult = syncIn(m_pInEndpoint, pRespBuffer, nRespBytes);
141 
143  if ((nResult < 0) ||
144  ((nResult < nRespBytes) && (!bWrite))) // == -Stall)
145  {
146  // STALL, clear the endpoint and attempt CSW read
147  bool bClearResult = false;
148  if (bWrite)
149  bClearResult = !clearEndpointHalt(m_pOutEndpoint);
150  else
151  bClearResult = !clearEndpointHalt(m_pInEndpoint);
152 
153  if (!bClearResult)
154  {
155  DEBUG_LOG("USB: MSD: Endpoint stalled, but clearing failed. "
156  "Performing reset.");
157 
158  // Reset and fail this command
159  massStorageReset();
161  clearEndpointHalt(m_pOutEndpoint);
162  return false;
163  }
164 
165  // Attempt to read the CSW now that the stall condition is cleared
166  Csw *pCsw = new Csw;
167  PointerGuard<Csw> cswGuard(pCsw);
168  nResult =
169  syncIn(m_pInEndpoint, reinterpret_cast<uintptr_t>(pCsw), 13);
170 
171  // Stalled?
172  if (nResult == -Stall)
173  {
174  // Perform full reset and reset both pipes
175  massStorageReset();
177  clearEndpointHalt(m_pOutEndpoint);
178 
179  // Failure condition
180  DEBUG_LOG("USB: MSD: Couldn't recover cleanly from endpoint "
181  "stall, mass storage reset completed");
182  return false;
183  }
184  else if (nResult < 0)
185  {
186  DEBUG_LOG(
187  "USB: MSD: Reading CSW after clearing stall ended up "
188  "failing with status "
189  << nResult);
190  return false;
191  }
192  else
193  {
194  DEBUG_LOG("USB: MSD: Recovered from endpoint stall");
195  return !pCsw->nStatus;
196  }
197  }
198 
199  if (nResult == 13)
200  {
201  Csw *pCsw = reinterpret_cast<Csw *>(pRespBuffer);
202  if (pCsw->nSig == CswSig)
203  {
204  DEBUG_LOG(
205  "USB: MSD: Early CSW with status "
206  << pCsw->nStatus << ", residue: " << pCsw->nResidue);
207  return !pCsw->nStatus;
208  }
209  }
210 
211  if (nResult < 0)
212  return false;
213  }
214 
215  Csw *pCsw = new Csw;
216  PointerGuard<Csw> guard2(pCsw);
217  nResult = syncIn(m_pInEndpoint, reinterpret_cast<uintptr_t>(pCsw), 13);
218 
220  if (nResult < 0)
221  {
223  {
224  massStorageReset();
226  {
227  DEBUG_LOG(
228  "USB: MSD: Reading CSW ended up failing after endpoint "
229  "halt cleared, and a mass storage reset, with status "
230  << nResult);
231  return false;
232  }
233  }
234  else
235  {
236  nResult =
237  syncIn(m_pInEndpoint, reinterpret_cast<uintptr_t>(pCsw), 13);
238  if (nResult < 0)
239  {
240  DEBUG_LOG(
241  "USB: MSD: Reading CSW ended up failing after endpoint "
242  "halt cleared, with status "
243  << nResult);
244  massStorageReset();
245  return false;
246  }
247  }
248  }
249 
250  return !pCsw->nStatus;
251 }
virtual void initialiseDriver()
Implemented by the driver class, initialises driver-specific stuff.
Endpoint * m_pInEndpoint
The endpoint used to receive input reports from the device.
virtual bool sendCommand(size_t nUnit, uintptr_t pCommand, uint8_t nCommandSize, uintptr_t pRespBuffer, uint16_t nRespBytes, bool bWrite)
bool controlRequest(uint8_t nRequestType, uint8_t nRequest, uint16_t nValue, uint16_t nIndex, uint16_t nLength=0, uintptr_t pBuffer=0)
Performs an USB control request.
Definition: UsbDevice.cc:424
UsbState m_UsbState
The current state of the device.
Definition: UsbDevice.h:310
Definition: Log.h:136
bool clearEndpointHalt(Endpoint *pEndpoint)
Clears a halt on the given endpoint.
Definition: UsbDevice.cc:520
#define ERROR(text)
Definition: Log.h:82
Definition: Log.h:138
#define DEBUG_LOG(text)
Definition: Log.h:69
Interface * m_pInterface
Interface in use.
Definition: UsbDevice.h:319