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