rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
mass_storage_device.cpp
Go to the documentation of this file.
1/**
2 * @file mass_storage_device.cpp
3 *
4 * @date April 9, 2021
5 * @author Matthew Kennedy, (c) 2021
6 *
7 * This file implements a USB mass storage device with multiple LUNs, so multiple drives can be mounted at once.
8 */
9
10#include "pch.h"
11
12#include "mass_storage_device.h"
13
14#if HAL_USE_USB_MSD
15
16#define MSD_REQ_RESET 0xFF
17#define MSD_REQ_GET_MAX_LUN 0xFE
18
19#define MSD_CBW_SIGNATURE 0x43425355
20#define MSD_CSW_SIGNATURE 0x53425355
21
22#define CBW_FLAGS_RESERVED_MASK 0b01111111
23#define CBW_LUN_RESERVED_MASK 0b11110000
24#define CBW_CMD_LEN_RESERVED_MASK 0b11000000
25
26#define CSW_STATUS_PASSED 0x00
27#define CSW_STATUS_FAILED 0x01
28#define CSW_STATUS_PHASE_ERROR 0x02
29
30#define MSD_SETUP_WORD(setup, index) (uint16_t)(((uint16_t)setup[index+1] << 8)\
31 | (setup[index] & 0x00FF))
32
33#define MSD_SETUP_VALUE(setup) MSD_SETUP_WORD(setup, 2)
34#define MSD_SETUP_INDEX(setup) MSD_SETUP_WORD(setup, 4)
35#define MSD_SETUP_LENGTH(setup) MSD_SETUP_WORD(setup, 6)
36
37static uint32_t scsi_transport_transmit(const SCSITransport *transport,
38 const uint8_t *data, size_t len) {
39 usb_scsi_transport_handler_t *trp = reinterpret_cast<usb_scsi_transport_handler_t*>(transport->handler);
40 msg_t status = usbTransmit(trp->usbp, trp->ep, data, len);
41 if (MSG_OK == status)
42 return len;
43 else
44 return 0;
45}
46
47static uint32_t scsi_transport_transmit_start(const SCSITransport *transport,
48 const uint8_t *data, size_t len) {
49 usb_scsi_transport_handler_t *trp = reinterpret_cast<usb_scsi_transport_handler_t*>(transport->handler);
50 msg_t status = usbTransmitStart(trp->usbp, trp->ep, data, len);
51 if (MSG_OK == status)
52 return len;
53 else
54 return 0;
55}
56
57static uint32_t scsi_transport_transmit_wait(const SCSITransport *transport) {
58 usb_scsi_transport_handler_t *trp = reinterpret_cast<usb_scsi_transport_handler_t*>(transport->handler);
59 msg_t status = usbTransmitWait(trp->usbp, trp->ep);
60 if (MSG_OK == status)
61 return 0;
62 else
63 return 1;
64}
65
66static uint32_t scsi_transport_receive(const SCSITransport *transport,
67 uint8_t *data, size_t len) {
68 usb_scsi_transport_handler_t *trp = reinterpret_cast<usb_scsi_transport_handler_t*>(transport->handler);
69 msg_t status = usbReceive(trp->usbp, trp->ep, data, len);
70 if (MSG_RESET != status)
71 return status;
72 else
73 return 0;
74}
75
77 : ThreadController("MSD", MSD_THD_PRIO)
78 , m_usb(usb) {
79 for (size_t i = 0; i < USB_MSD_LUN_COUNT; i++) {
80 scsiObjectInit(&m_luns[i].target);
81 }
82
83 m_scsiTransportHandler.usbp = usb;
84 m_scsiTransportHandler.ep = USB_MSD_DATA_EP;
90}
91
93 while (!chThdShouldTerminateX()) {
94 const msg_t status = usbReceive(m_usb, USB_MSD_DATA_EP, (uint8_t*)&m_cbw, sizeof(m_cbw));
95
96 if (MSG_RESET == status) {
97 chThdSleepMilliseconds(50);
98 continue;
99 }
100
101 if (cbwValid(m_cbw, status) && cbwMeaningful(m_cbw)) {
102 auto target = &m_luns[m_cbw.lun].target;
103 if (SCSI_SUCCESS == scsiExecCmd(target, m_cbw.cmd_data)) {
104 sendCsw(CSW_STATUS_PASSED, 0);
105 } else {
106 sendCsw(CSW_STATUS_FAILED, scsiResidue(target));
107 }
108 } else {
109 // ignore incorrect CBW
110 }
111 }
112}
113
114/*static*/ bool MassStorageController::cbwValid(const msd_cbw_t& cbw, msg_t recvd) {
115 // check valid size
116 if (sizeof(msd_cbw_t) != recvd) {
117 return false;
118 }
119
120 // Check valid signature
121 if (cbw.signature != MSD_CBW_SIGNATURE) {
122 return false;
123 }
124
125 return true;
126}
127
128/*static*/ bool MassStorageController::cbwMeaningful(const msd_cbw_t& cbw) {
129 if ((cbw.cmd_len & CBW_CMD_LEN_RESERVED_MASK) != 0) {
130 return false;
131 }
132
133 if ((cbw.flags & CBW_FLAGS_RESERVED_MASK) != 0) {
134 return false;
135 }
136
137 if (cbw.lun >= USB_MSD_LUN_COUNT) {
138 return false;
139 }
140
141 return true;
142}
143
144void MassStorageController::sendCsw(uint8_t status, uint32_t residue) {
145 m_csw.signature = MSD_CSW_SIGNATURE;
146 m_csw.data_residue = residue;
147 m_csw.tag = m_cbw.tag;
148 m_csw.status = status;
149
150 usbTransmit(m_usb, USB_MSD_DATA_EP, (uint8_t*)&m_csw, sizeof(m_csw));
151}
152
153/**
154 * @brief Hardcoded default SCSI inquiry response structure.
155 */
156static const scsi_inquiry_response_t default_scsi_inquiry_response = {
157 0x00, /* direct access block device */
158 0x80, /* removable */
159 0x04, /* SPC-2 */
160 0x02, /* response data format */
161 0x20, /* response has 0x20 + 4 bytes */
162 0x00,
163 0x00,
164 0x00,
165 "Chibios",
166 "Mass Storage",
167 {'v',CH_KERNEL_MAJOR+'0','.',CH_KERNEL_MINOR+'0'}
168};
169
170/**
171 * @brief Hardcoded default SCSI unit serial number inquiry response structure.
172 */
173static const scsi_unit_serial_number_inquiry_response_t default_scsi_unit_serial_number_inquiry_response =
174{
175 0x00,
176 0x80,
177 0x00,
178 0x08,
179 "0000000"
180};
181
183 BaseBlockDevice *blkdev, uint8_t *blkbuf, size_t blkbufsize,
184 const scsi_inquiry_response_t *inquiry,
185 const scsi_unit_serial_number_inquiry_response_t *serialInquiry) {
186 chibios_rt::MutexLocker lock(m_lunMutex);
187
188 auto& lun = m_luns[lunIndex];
189
190
191 if (NULL == inquiry) {
192 lun.config.inquiry_response = &default_scsi_inquiry_response;
193 }
194 else {
195 lun.config.inquiry_response = inquiry;
196 }
197 if (NULL == serialInquiry) {
198 lun.config.unit_serial_number_inquiry_response = &default_scsi_unit_serial_number_inquiry_response;
199 }
200 else {
201 lun.config.unit_serial_number_inquiry_response = serialInquiry;
202 }
203 lun.config.blkbuf = blkbuf;
204 lun.config.blkbufsize = blkbufsize;
205 lun.config.blkdev = blkdev;
206 lun.config.transport = &m_scsiTransport;
207
208 scsiStart(&lun.target, &lun.config);
209}
210
211bool msd_request_hook_new(USBDriver *usbp) {
212 /* check that the request is for interface 0.*/
213 if (MSD_SETUP_INDEX(usbp->setup) != 0) {
214 return false;
215 }
216
217 if (usbp->setup[0] == (USB_RTYPE_TYPE_CLASS | USB_RTYPE_RECIPIENT_INTERFACE | USB_RTYPE_DIR_HOST2DEV)
218 && usbp->setup[1] == MSD_REQ_RESET) {
219 /* Bulk-Only Mass Storage Reset (class-specific request)
220 This request is used to reset the mass storage device and its associated interface.
221 This class-specific request shall ready the device for the next CBW from the host. */
222 /* Do any special reset code here. */
223 /* The device shall NAK the status stage of the device request until
224 * the Bulk-Only Mass Storage Reset is complete.
225 * NAK EP1 in and out */
226 // usbp->otg->ie[1].DIEPCTL = DIEPCTL_SNAK;
227 // usbp->otg->oe[1].DOEPCTL = DOEPCTL_SNAK;
228 /* response to this request using EP0 */
229 usbSetupTransfer(usbp, 0, 0, NULL);
230 return true;
231 } else if (usbp->setup[0] == (USB_RTYPE_TYPE_CLASS | USB_RTYPE_RECIPIENT_INTERFACE | USB_RTYPE_DIR_DEV2HOST)
232 && usbp->setup[1] == MSD_REQ_GET_MAX_LUN) {
233 /* Return the maximum supported LUN. */
234 static uint8_t zero = USB_MSD_LUN_COUNT - 1;
235 usbSetupTransfer(usbp, &zero, 1, NULL);
236 return true;
237 }
238
239 return false;
240}
241
242#endif // HAL_USE_USB_MSD
void sendCsw(uint8_t status, uint32_t residue)
void attachLun(uint8_t lunIndex, BaseBlockDevice *blkdev, uint8_t *blkbuf, size_t blkbufsize, const scsi_inquiry_response_t *inquiry, const scsi_unit_serial_number_inquiry_response_t *serialInquiry)
LunEntry m_luns[USB_MSD_LUN_COUNT]
MassStorageController(USBDriver *usb)
static bool cbwValid(const msd_cbw_t &cbw, msg_t status)
chibios_rt::Mutex m_lunMutex
static bool cbwMeaningful(const msd_cbw_t &cbw)
usb_scsi_transport_handler_t m_scsiTransportHandler
A base class for a controller that requires its own thread.
bool msd_request_hook_new(USBDriver *usbp)
static const scsi_unit_serial_number_inquiry_response_t default_scsi_unit_serial_number_inquiry_response
Hardcoded default SCSI unit serial number inquiry response structure.
static const scsi_inquiry_response_t default_scsi_inquiry_response
Hardcoded default SCSI inquiry response structure.
static uint32_t scsi_transport_transmit_start(const SCSITransport *transport, const uint8_t *data, size_t len)
static uint32_t scsi_transport_transmit_wait(const SCSITransport *transport)
static uint32_t scsi_transport_receive(const SCSITransport *transport, uint8_t *data, size_t len)
static uint32_t scsi_transport_transmit(const SCSITransport *transport, const uint8_t *data, size_t len)
uint8_t blkbuf[4 *MMCSD_BLOCK_SIZE]
Definition mmc_card.cpp:292
SCSITarget target
SCSITargetConfig config