c94aaa88cd1ad867ec47939488fbe23026c26aef
[SCSI2SD.git] / software / SCSI2SD / SCSI2SD.cydsn / disk.c
1 // Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
2 //
3 // This file is part of SCSI2SD.
4 //
5 // SCSI2SD is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // SCSI2SD is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with SCSI2SD. If not, see <http://www.gnu.org/licenses/>.
17
18 #include "device.h"
19 #include "scsi.h"
20 #include "config.h"
21 #include "disk.h"
22 #include "sd.h"
23
24 #include <string.h>
25
26 // Global
27 BlockDevice blockDev;
28 Transfer transfer;
29
30 static int doSdInit()
31 {
32 int result = sdInit();
33 if (result)
34 {
35 blockDev.state = blockDev.state | DISK_INITIALISED;
36
37 // artificially limit this value according to EEPROM config.
38 blockDev.capacity =
39 (config->maxBlocks && (sdDev.capacity > config->maxBlocks))
40 ? config->maxBlocks : sdDev.capacity;
41 }
42 return result;
43 }
44
45
46 static void doFormatUnit()
47 {
48 // Low-level formatting is not required.
49 // Nothing left to do.
50 }
51
52 static void doReadCapacity()
53 {
54 uint32 lba = (((uint32) scsiDev.cdb[2]) << 24) +
55 (((uint32) scsiDev.cdb[3]) << 16) +
56 (((uint32) scsiDev.cdb[4]) << 8) +
57 scsiDev.cdb[5];
58 int pmi = scsiDev.cdb[8] & 1;
59
60 if (!pmi && lba)
61 {
62 // error.
63 // We don't do anything with the "partial medium indicator", and
64 // assume that delays are constant across each block. But the spec
65 // says we must return this error if pmi is specified incorrectly.
66 scsiDev.status = CHECK_CONDITION;
67 scsiDev.sense.code = ILLEGAL_REQUEST;
68 scsiDev.sense.asc = INVALID_FIELD_IN_CDB;
69 scsiDev.phase = STATUS;
70 }
71 else if (blockDev.capacity > 0)
72 {
73 uint32 highestBlock = blockDev.capacity - 1;
74
75 scsiDev.data[0] = highestBlock >> 24;
76 scsiDev.data[1] = highestBlock >> 16;
77 scsiDev.data[2] = highestBlock >> 8;
78 scsiDev.data[3] = highestBlock;
79
80 scsiDev.data[4] = blockDev.bs >> 24;
81 scsiDev.data[5] = blockDev.bs >> 16;
82 scsiDev.data[6] = blockDev.bs >> 8;
83 scsiDev.data[7] = blockDev.bs;
84 scsiDev.dataLen = 8;
85 scsiDev.phase = DATA_IN;
86 }
87 else
88 {
89 scsiDev.status = CHECK_CONDITION;
90 scsiDev.sense.code = NOT_READY;
91 scsiDev.sense.asc = MEDIUM_NOT_PRESENT;
92 scsiDev.phase = STATUS;
93 }
94 }
95
96 static void doWrite(uint32 lba, uint32 blocks)
97 {
98 if (blockDev.state & DISK_WP)
99 {
100 scsiDev.status = CHECK_CONDITION;
101 scsiDev.sense.code = ILLEGAL_REQUEST;
102 scsiDev.sense.asc = WRITE_PROTECTED;
103 scsiDev.phase = STATUS;
104 }
105 else if (((uint64) lba) + blocks > blockDev.capacity)
106 {
107 scsiDev.status = CHECK_CONDITION;
108 scsiDev.sense.code = ILLEGAL_REQUEST;
109 scsiDev.sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
110 scsiDev.phase = STATUS;
111 }
112 else
113 {
114 transfer.dir = TRANSFER_WRITE;
115 transfer.lba = lba;
116 transfer.blocks = blocks;
117 transfer.currentBlock = 0;
118 scsiDev.phase = DATA_OUT;
119 scsiDev.dataLen = SCSI_BLOCK_SIZE;
120 scsiDev.dataPtr = SCSI_BLOCK_SIZE; // TODO FIX scsiDiskPoll()
121
122 // No need for single-block reads atm. Overhead of the
123 // multi-block read is minimal.
124 transfer.multiBlock = 1;
125 sdPrepareWrite();
126 }
127 }
128
129
130 static void doRead(uint32 lba, uint32 blocks)
131 {
132 if (((uint64) lba) + blocks > blockDev.capacity)
133 {
134 scsiDev.status = CHECK_CONDITION;
135 scsiDev.sense.code = ILLEGAL_REQUEST;
136 scsiDev.sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
137 scsiDev.phase = STATUS;
138 }
139 else
140 {
141 transfer.dir = TRANSFER_READ;
142 transfer.lba = lba;
143 transfer.blocks = blocks;
144 transfer.currentBlock = 0;
145 scsiDev.phase = DATA_IN;
146 scsiDev.dataLen = 0; // No data yet
147
148 if ((blocks == 1) ||
149 (((uint64) lba) + blocks == blockDev.capacity)
150 )
151 {
152 // We get errors on reading the last sector using a multi-sector
153 // read :-(
154 transfer.multiBlock = 0;
155 }
156 else
157 {
158 transfer.multiBlock = 1;
159 sdPrepareRead();
160 }
161 }
162 }
163
164 static void doSeek(uint32 lba)
165 {
166 if (lba >= blockDev.capacity)
167 {
168 scsiDev.status = CHECK_CONDITION;
169 scsiDev.sense.code = ILLEGAL_REQUEST;
170 scsiDev.sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
171 scsiDev.phase = STATUS;
172 }
173 }
174
175 static int doTestUnitReady()
176 {
177 int ready = 1;
178 if (!(blockDev.state & DISK_STARTED))
179 {
180 ready = 0;
181 scsiDev.status = CHECK_CONDITION;
182 scsiDev.sense.code = NOT_READY;
183 scsiDev.sense.asc = LOGICAL_UNIT_NOT_READY_INITIALIZING_COMMAND_REQUIRED;
184 scsiDev.phase = STATUS;
185 }
186 else if (!(blockDev.state & DISK_PRESENT))
187 {
188 ready = 0;
189 scsiDev.status = CHECK_CONDITION;
190 scsiDev.sense.code = NOT_READY;
191 scsiDev.sense.asc = MEDIUM_NOT_PRESENT;
192 scsiDev.phase = STATUS;
193 }
194 else if (!(blockDev.state & DISK_INITIALISED))
195 {
196 ready = 0;
197 scsiDev.status = CHECK_CONDITION;
198 scsiDev.sense.code = NOT_READY;
199 scsiDev.sense.asc = LOGICAL_UNIT_NOT_READY_CAUSE_NOT_REPORTABLE;
200 scsiDev.phase = STATUS;
201 }
202 return ready;
203 }
204
205 // Handle direct-access scsi device commands
206 int scsiDiskCommand()
207 {
208 int commandHandled = 1;
209
210 uint8 command = scsiDev.cdb[0];
211 if (command == 0x1B)
212 {
213 // START STOP UNIT
214 // Enable or disable media access operations.
215 // Ignore load/eject requests. We can't do that.
216 //int immed = scsiDev.cdb[1] & 1;
217 int start = scsiDev.cdb[4] & 1;
218
219 if (start)
220 {
221 blockDev.state = blockDev.state | DISK_STARTED;
222 if (!(blockDev.state & DISK_INITIALISED))
223 {
224 doSdInit();
225 }
226 }
227 else
228 {
229 blockDev.state &= ~DISK_STARTED;
230 }
231 }
232 else if (command == 0x00)
233 {
234 // TEST UNIT READY
235 doTestUnitReady();
236 }
237 else if (!doTestUnitReady())
238 {
239 // Status and sense codes already set by doTestUnitReady
240 }
241 else if (command == 0x04)
242 {
243 // FORMAT UNIT
244 doFormatUnit();
245 }
246 else if (command == 0x08)
247 {
248 // READ(6)
249 uint32 lba =
250 (((uint32) scsiDev.cdb[1] & 0x1F) << 16) +
251 (((uint32) scsiDev.cdb[2]) << 8) +
252 scsiDev.cdb[3];
253 uint32 blocks = scsiDev.cdb[4];
254 if (blocks == 0) blocks = 256;
255 doRead(lba, blocks);
256 }
257
258 else if (command == 0x28)
259 {
260 // READ(10)
261 // Ignore all cache control bits - we don't support a memory cache.
262
263 uint32 lba =
264 (((uint32) scsiDev.cdb[2]) << 24) +
265 (((uint32) scsiDev.cdb[3]) << 16) +
266 (((uint32) scsiDev.cdb[4]) << 8) +
267 scsiDev.cdb[5];
268 uint32 blocks =
269 (((uint32) scsiDev.cdb[7]) << 8) +
270 scsiDev.cdb[8];
271
272 doRead(lba, blocks);
273 }
274
275 else if (command == 0x25)
276 {
277 // READ CAPACITY
278 doReadCapacity();
279 }
280
281 else if (command == 0x0B)
282 {
283 // SEEK(6)
284 uint32 lba =
285 (((uint32) scsiDev.cdb[1] & 0x1F) << 16) +
286 (((uint32) scsiDev.cdb[2]) << 8) +
287 scsiDev.cdb[3];
288
289 doSeek(lba);
290 }
291
292 else if (command == 0x2B)
293 {
294 // SEEK(10)
295 uint32 lba =
296 (((uint32) scsiDev.cdb[2]) << 24) +
297 (((uint32) scsiDev.cdb[3]) << 16) +
298 (((uint32) scsiDev.cdb[4]) << 8) +
299 scsiDev.cdb[5];
300
301 doSeek(lba);
302 }
303 else if (command == 0x0A)
304 {
305 // WRITE(6)
306 uint32 lba =
307 (((uint32) scsiDev.cdb[1] & 0x1F) << 16) +
308 (((uint32) scsiDev.cdb[2]) << 8) +
309 scsiDev.cdb[3];
310 uint32 blocks = scsiDev.cdb[4];
311 if (blocks == 0) blocks = 256;
312 doWrite(lba, blocks);
313 }
314
315 else if (command == 0x2A)
316 {
317 // WRITE(10)
318 // Ignore all cache control bits - we don't support a memory cache.
319
320 uint32 lba =
321 (((uint32) scsiDev.cdb[2]) << 24) +
322 (((uint32) scsiDev.cdb[3]) << 16) +
323 (((uint32) scsiDev.cdb[4]) << 8) +
324 scsiDev.cdb[5];
325 uint32 blocks =
326 (((uint32) scsiDev.cdb[7]) << 8) +
327 scsiDev.cdb[8];
328
329 doWrite(lba, blocks);
330 }
331 else if (command == 0x36)
332 {
333 // LOCK UNLOCK CACHE
334 // We don't have a cache to lock data into. do nothing.
335 }
336 else if (command == 0x34)
337 {
338 // PRE-FETCH.
339 // We don't have a cache to pre-fetch into. do nothing.
340 }
341 else if (command == 0x1E)
342 {
343 // PREVENT ALLOW MEDIUM REMOVAL
344 // Not much we can do to prevent the user removing the SD card.
345 // do nothing.
346 }
347 else if (command == 0x01)
348 {
349 // REZERO UNIT
350 // Set the lun to a vendor-specific state. Ignore.
351 }
352 else if (command == 0x35)
353 {
354 // SYNCHRONIZE CACHE
355 // We don't have a cache. do nothing.
356 }
357 else
358 {
359 commandHandled = 0;
360 }
361
362 return commandHandled;
363 }
364
365 void scsiDiskPoll()
366 {
367 if (scsiDev.phase == DATA_IN &&
368 transfer.currentBlock != transfer.blocks)
369 {
370 if (scsiDev.dataLen == 0)
371 {
372 if (transfer.multiBlock)
373 {
374 sdReadSectorMulti();
375 }
376 else
377 {
378 sdReadSectorSingle();
379 }
380 }
381 else if (scsiDev.dataPtr == scsiDev.dataLen)
382 {
383 scsiDev.dataLen = 0;
384 scsiDev.dataPtr = 0;
385 transfer.currentBlock++;
386 if (transfer.currentBlock >= transfer.blocks)
387 {
388 int needComplete = transfer.multiBlock;
389 scsiDev.phase = STATUS;
390 scsiDiskReset();
391 if (needComplete)
392 {
393 sdCompleteRead();
394 }
395 }
396 }
397 }
398 else if (scsiDev.phase == DATA_OUT &&
399 transfer.currentBlock != transfer.blocks)
400 {
401 int writeOk = sdWriteSector();
402 // TODO FIX scsiDiskPoll() scsiDev.dataPtr = 0;
403 transfer.currentBlock++;
404 if (transfer.currentBlock >= transfer.blocks)
405 {
406 scsiDev.dataLen = 0;
407 scsiDev.dataPtr = 0;
408 scsiDev.phase = STATUS;
409
410 scsiDiskReset();
411
412 if (writeOk)
413 {
414 sdCompleteWrite();
415 }
416 }
417 }
418 }
419
420 void scsiDiskReset()
421 {
422 // todo if SPI command in progress, cancel it.
423 scsiDev.dataPtr = 0;
424 scsiDev.savedDataPtr = 0;
425 scsiDev.dataLen = 0;
426 transfer.lba = 0;
427 transfer.blocks = 0;
428 transfer.currentBlock = 0;
429 transfer.multiBlock = 0;
430 }
431
432 void scsiDiskInit()
433 {
434 blockDev.bs = SCSI_BLOCK_SIZE;
435 blockDev.capacity = 0;
436 scsiDiskReset();
437
438 // Don't require the host to send us a START STOP UNIT command
439 blockDev.state = DISK_STARTED;
440 // WP pin not available for micro-sd
441 // TODO read card WP register
442 #if 0
443 if (SD_WP_Read())
444 {
445 blockDev.state = blockDev.state | DISK_WP;
446 }
447 #endif
448
449 if (SD_CD_Read() == 1)
450 {
451 int retry;
452 blockDev.state = blockDev.state | DISK_PRESENT;
453
454 // Wait up to 5 seconds for the SD card to wake up.
455 for (retry = 0; retry < 5; ++retry)
456 {
457 if (doSdInit())
458 {
459 break;
460 }
461 else
462 {
463 CyDelay(1000);
464 }
465 }
466 }
467 }
468