openMSX
SdCard.cc
Go to the documentation of this file.
1#include "SdCard.hh"
2#include "DeviceConfig.hh"
3#include "HD.hh"
4#include "MSXException.hh"
5#include "unreachable.hh"
6#include "endian.hh"
7#include "narrow.hh"
8#include "serialize.hh"
9#include "serialize_stl.hh"
10#include <memory>
11
12// TODO:
13// - replace transferDelayCounter with 0xFF's in responseQueue?
14// - remove duplication between WRITE and MULTI_WRITE (is it worth it?)
15// - see TODOs in the code below
16
17namespace openmsx {
18
19// data response tokens
20static constexpr byte DRT_ACCEPTED = 0x05;
21static constexpr byte DRT_WRITE_ERROR = 0x0D;
22
23// start block tokens and stop tran token
24static constexpr byte START_BLOCK_TOKEN = 0xFE;
25static constexpr byte START_BLOCK_TOKEN_MBW = 0xFC;
26static constexpr byte STOP_TRAN_TOKEN = 0xFD;
27
28// data error token
29static constexpr byte DATA_ERROR_TOKEN_ERROR = 0x01;
30static constexpr byte DATA_ERROR_TOKEN_OUT_OF_RANGE = 0x08;
31
32// responses
33static constexpr byte R1_BUSY = 0x00;
34static constexpr byte R1_IDLE = 0x01; // TODO: why is lots of code checking for this instead of R1_BUSY?
35static constexpr byte R1_ILLEGAL_COMMAND = 0x04;
36static constexpr byte R1_PARAMETER_ERROR = 0x80;
37
39 : hd(config.getXML() ? std::make_unique<HD>(config) : nullptr)
40{
41}
42
43SdCard::~SdCard() = default;
44
45// helper methods for 'transfer' to avoid duplication
46byte SdCard::readCurrentByteFromCurrentSector()
47{
48 byte result = [&] {
49 if (currentByteInSector == -1) {
50 try {
51 hd->readSector(currentSector, sectorBuf);
52 return START_BLOCK_TOKEN;
53 } catch (MSXException&) {
54 return DATA_ERROR_TOKEN_ERROR;
55 }
56 } else {
57 // output next byte from stream
58 return sectorBuf.raw[currentByteInSector];
59 }
60 }();
61 currentByteInSector++;
62 if (currentByteInSector == sizeof(sectorBuf)) {
63 responseQueue.push_back({0x00, 0x00}); // 2 CRC's (dummy)
64 }
65 return result;
66}
67
68byte SdCard::transfer(byte value, bool cs)
69{
70 if (!hd) return 0xFF; // no card inserted
71
72 if (cs) {
73 // /CS is true: not for this chip
74 return 0xFF;
75 }
76
77 // process output
78 byte retval = 0xFF;
79 if (transferDelayCounter > 0) {
80 --transferDelayCounter;
81 } else {
82 if (responseQueue.empty()) {
83 switch (mode) {
84 case READ:
85 retval = readCurrentByteFromCurrentSector();
86 if (currentByteInSector == sizeof(sectorBuf)) {
87 mode = COMMAND;
88 }
89 break;
90 case MULTI_READ:
91 // when there's an error, you probably have to send a CMD12
92 // to go back to the COMMAND mode. This is not
93 // clear in the spec (it's wrongly suggested
94 // for the MULTI_WRITE mode!)
95 if (currentSector >= hd->getNbSectors()) {
96 // data out of range, send data error token
97 retval = DATA_ERROR_TOKEN_OUT_OF_RANGE;
98 } else {
99 retval = readCurrentByteFromCurrentSector();
100 if (currentByteInSector == sizeof(sectorBuf)) {
101 currentSector++;
102 currentByteInSector = -1;
103 }
104 }
105 break;
106 case WRITE:
107 case MULTI_WRITE:
108 // apparently nothing is returned while writing
109 case COMMAND:
110 default:
111 break;
112 }
113 } else {
114 retval = responseQueue.pop_front();
115 }
116 }
117
118 // process input
119 switch (mode) {
120 case WRITE:
121 // first check for data token
122 if (currentByteInSector == -1) {
123 if (value == START_BLOCK_TOKEN) {
124 currentByteInSector++;
125 }
126 break;
127 }
128 if (currentByteInSector < int(sizeof(sectorBuf))) {
129 sectorBuf.raw[currentByteInSector] = value;
130 }
131 currentByteInSector++;
132 if (currentByteInSector == (sizeof(sectorBuf) + 2)) {
133 byte response = DRT_ACCEPTED;
134 // copy buffer to SD card
135 try {
136 hd->writeSector(currentSector, sectorBuf);
137 } catch (MSXException&) {
138 response = DRT_WRITE_ERROR;
139 }
140 mode = COMMAND;
141 transferDelayCounter = 1;
142 responseQueue.push_back(response);
143 }
144 break;
145 case MULTI_WRITE:
146 // first check for stop or start token
147 if (currentByteInSector == -1) {
148 if (value == STOP_TRAN_TOKEN) {
149 mode = COMMAND;
150 }
151 if (value == START_BLOCK_TOKEN_MBW) {
152 currentByteInSector++;
153 }
154 break;
155 }
156 if (currentByteInSector < int(sizeof(sectorBuf))) {
157 sectorBuf.raw[currentByteInSector] = value;
158 }
159 currentByteInSector++;
160 if (currentByteInSector == (sizeof(sectorBuf) + 2)) {
161 // check if still in valid range
162 byte response = DRT_ACCEPTED;
163 if (currentSector >= hd->getNbSectors()) {
164 response = DRT_WRITE_ERROR;
165 // note: mode is not changed, should be done by
166 // the host with CMD12 (STOP_TRANSMISSION) -
167 // however, this makes no sense, CMD12 is only
168 // for Multiple Block Read!? Wrong in the spec?
169 } else {
170 // copy buffer to SD card
171 try {
172 hd->writeSector(currentSector, sectorBuf);
173 currentByteInSector = -1;
174 currentSector++;
175 } catch (MSXException&) {
176 response = DRT_WRITE_ERROR;
177 // note: mode is not changed, should be
178 // done by the host with CMD12
179 // (STOP_TRANSMISSION) - however, this
180 // makes no sense, CMD12 is only for
181 // Multiple Block Read!? Wrong in the
182 // spec?
183 }
184 }
185 transferDelayCounter = 1;
186 responseQueue.push_back(response);
187 }
188 break;
189 case COMMAND:
190 default:
191 if ((cmdIdx == 0 && (value >> 6) == 1) // command start
192 || cmdIdx > 0) { // command in progress
193 cmdBuf[cmdIdx] = value;
194 ++cmdIdx;
195 if (cmdIdx == 6) {
196 executeCommand();
197 cmdIdx = 0;
198 }
199 }
200 break;
201 }
202
203 return retval;
204}
205
206void SdCard::executeCommand()
207{
208 // it takes 2 transfers (2x8 cycles) before a reply
209 // can be given to a command
210 transferDelayCounter = 2;
211 byte command = cmdBuf[0] & 0x3F;
212 switch (command) {
213 case 0: // GO_IDLE_STATE
214 responseQueue.clear();
215 mode = COMMAND;
216 responseQueue.push_back(R1_IDLE);
217 break;
218 case 8: // SEND_IF_COND
219 // conditions are always OK
220 responseQueue.push_back({
221 R1_IDLE, // R1 (OK) SDHC (checked by MegaSD and FUZIX)
222 byte(0x02), // command version
223 byte(0x00), // reserved
224 byte(0x01), // voltage accepted
225 cmdBuf[4]});// check pattern
226 break;
227 case 9:{ // SEND_CSD
228 responseQueue.push_back({
229 R1_BUSY, // OK (ignored on MegaSD code, used in FUZIX)
230 // now follows a CSD version 2.0 (for SDHC)
231 START_BLOCK_TOKEN, // data token
232 byte(0x40), // CSD_STRUCTURE [127:120]
233 byte(0x0E), // (TAAC)
234 byte(0x00), // (NSAC)
235 byte(0x32), // (TRAN_SPEED)
236 byte(0x00), // CCC
237 byte(0x00), // CCC / (READ_BL_LEN)
238 byte(0x00)}); // (RBP)/(WBM)/(RBM)/ DSR_IMP
239 // SD_CARD_SIZE = (C_SIZE + 1) * 512kByte
240 auto c_size = narrow<uint32_t>((hd->getNbSectors() * sizeof(sectorBuf)) / size_t(512 * 1024) - 1);
241 responseQueue.push_back({
242 byte((c_size >> 16) & 0x3F), // C_SIZE 1
243 byte((c_size >> 8) & 0xFF), // C_SIZE 2
244 byte((c_size >> 0) & 0xFF), // C_SIZE 3
245 byte(0x00), // res/(EBE)/(SS1)
246 byte(0x00), // (SS2)/(WGS)
247 byte(0x00), // (WGE)/res/(RF)/(WBL1)
248 byte(0x00), // (WBL2)/(WBP)/res
249 byte(0x00), // (FFG)/COPY/PWP/TWP/(FF)/res
250 byte(0x01)}); // CRC / 1
251 break;}
252 case 10: // SEND_CID
253 responseQueue.push_back({
254 R1_BUSY, // OK (ignored on MegaSD, unused in FUZIX so far)
255 START_BLOCK_TOKEN, // data token
256 byte(0xAA), // CID01 // manuf ID
257 byte('o' ), // CID02 // OEM/App ID 1
258 byte('p' ), // CID03 // OEM/App ID 2
259 byte('e' ), // CID04 // Prod name 1
260 byte('n' ), // CID05 // Prod name 2
261 byte('M' ), // CID06 // Prod name 3
262 byte('S' ), // CID07 // Prod name 4
263 byte('X' ), // CID08 // Prod name 5
264 byte(0x01), // CID09 // Prod Revision
265 byte(0x12), // CID10 // Prod Serial 1
266 byte(0x34), // CID11 // Prod Serial 2
267 byte(0x56), // CID12 // Prod Serial 3
268 byte(0x78), // CID13 // Prod Serial 4
269 byte(0x00), // CID14 // reserved / Y1
270 byte(0xE6), // CID15 // Y2 / M
271 byte(0x01)}); // CID16 // CRC / not used
272 break;
273 case 12: // STOP TRANSMISSION
274 responseQueue.push_back(R1_IDLE); // R1 (OK)
275 mode = COMMAND;
276 break;
277 case 16: // SET_BLOCK_LEN
278 responseQueue.push_back(R1_IDLE); // OK, we don't really care
279 break;
280 case 17: // READ_SINGLE_BLOCK
281 case 18: // READ_MULTIPLE_BLOCK
282 case 24: // WRITE_BLOCK
283 case 25: // WRITE_MULTIPLE_BLOCK
284 // SDHC so the address is the sector
285 currentSector = Endian::readB32(&cmdBuf[1]);
286 if (currentSector >= hd->getNbSectors()) {
287 responseQueue.push_back(R1_PARAMETER_ERROR);
288 } else {
289 responseQueue.push_back(R1_BUSY);
290 switch (command) {
291 case 17: mode = READ; break;
292 case 18: mode = MULTI_READ; break;
293 case 24: mode = WRITE; break;
294 case 25: mode = MULTI_WRITE; break;
295 default: UNREACHABLE;
296 }
297 currentByteInSector = -1; // wait for token
298 }
299 break;
300 case 41: // implementation of ACMD 41!!
301 // SD_SEND_OP_COND
302 responseQueue.push_back(R1_BUSY);
303 break;
304 case 55: // APP_CMD
305 // TODO: go to ACMD mode, but not necessary now
306 responseQueue.push_back(R1_IDLE);
307 break;
308 case 58: // READ_OCR
309 responseQueue.push_back({
310 R1_BUSY,// R1 (OK) (ignored on MegaSD, checked in FUZIX)
311 byte(0x40), // OCR Reg part 1 (SDHC: CCS=1)
312 byte(0x00), // OCR Reg part 2
313 byte(0x00), // OCR Reg part 3
314 byte(0x00)}); // OCR Reg part 4
315 break;
316
317 default:
318 responseQueue.push_back(R1_ILLEGAL_COMMAND);
319 break;
320 }
321}
322
323static constexpr std::initializer_list<enum_string<SdCard::Mode>> modeInfo = {
324 { "COMMAND", SdCard::COMMAND },
325 { "READ", SdCard::READ },
326 { "MULTI_READ", SdCard::MULTI_READ },
327 { "WRITE", SdCard::WRITE },
328 { "MULTI_WRITE", SdCard::MULTI_WRITE }
329};
331
332template<typename Archive>
333void SdCard::serialize(Archive& ar, unsigned /*version*/)
334{
335 ar.serialize("mode", mode,
336 "cmdBuf", cmdBuf);
337 ar.serialize_blob("sectorBuf", sectorBuf.raw);
338 if (hd) ar.serialize("hd", *hd);
339 ar.serialize("cmdIdx", cmdIdx,
340 "transferDelayCounter", transferDelayCounter,
341 "responseQueue", responseQueue,
342 "currentSector", currentSector,
343 "currentByteInSector", currentByteInSector);
344}
346
347} // namespace openmsx
bool empty() const
void push_back(U &&u)
SdCard(const DeviceConfig &config)
Definition: SdCard.cc:38
void serialize(Archive &ar, unsigned version)
Definition: SdCard.cc:333
byte transfer(byte value, bool cs)
Definition: SdCard.cc:68
uint32_t readB32(const void *p)
Definition: endian.hh:163
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
STL namespace.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1021
std::array< uint8_t, 512 > raw
#define UNREACHABLE
Definition: unreachable.hh:38