Disable 20MB/s support until data corruption issue is fixed.
[SCSI2SD-V6.git] / src / firmware / scsi.c
1 //      Copyright (C) 2014 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 "scsi.h"\r
19 #include "scsiPhy.h"\r
20 #include "config.h"\r
21 #include "diagnostic.h"\r
22 #include "disk.h"\r
23 #include "inquiry.h"\r
24 #include "led.h"\r
25 #include "mode.h"\r
26 #include "time.h"\r
27 #include "bsp.h"\r
28 #include "cdrom.h"\r
29 //#include "debug.h"\r
30 #include "tape.h"\r
31 #include "mo.h"\r
32 \r
33 #include <string.h>\r
34 \r
35 // Global SCSI device state.\r
36 ScsiDevice scsiDev S2S_DMA_ALIGN;\r
37 \r
38 static void enter_SelectionPhase(void);\r
39 static void process_SelectionPhase(void);\r
40 static void enter_BusFree(void);\r
41 static void enter_MessageIn(uint8_t message);\r
42 static void enter_Status(uint8_t status);\r
43 static void enter_DataIn(int len);\r
44 static void process_DataIn(void);\r
45 static void process_DataOut(void);\r
46 static void process_Command(void);\r
47 \r
48 static void doReserveRelease(void);\r
49 \r
50 static void enter_BusFree()\r
51 {\r
52         // This delay probably isn't needed for most SCSI hosts, but it won't\r
53         // hurt either. It's possible some of the samplers needed this delay.\r
54         if (scsiDev.compatMode < COMPAT_SCSI2)\r
55         {\r
56                 s2s_delay_us(2);\r
57         }\r
58 \r
59 #if 0\r
60         if (scsiDev.status != GOOD && isDebugEnabled())\r
61         {\r
62                 // We want to capture debug information for failure cases.\r
63                 s2s_delay_ms(64);\r
64         }\r
65 #endif\r
66 \r
67         scsiEnterBusFree();\r
68 \r
69         // Wait for the initiator to cease driving signals\r
70         // Bus settle delay + bus clear delay = 1200ns\r
71         s2s_delay_us(2);\r
72 \r
73 \r
74         s2s_ledOff();\r
75         scsiDev.phase = BUS_FREE;\r
76 }\r
77 \r
78 static void enter_MessageIn(uint8_t message)\r
79 {\r
80         scsiDev.msgIn = message;\r
81         scsiDev.phase = MESSAGE_IN;\r
82 }\r
83 \r
84 void process_MessageIn()\r
85 {\r
86         scsiEnterPhase(MESSAGE_IN);\r
87         scsiWriteByte(scsiDev.msgIn);\r
88 \r
89         if (unlikely(scsiDev.atnFlag))\r
90         {\r
91                 // If there was a parity error, we go\r
92                 // back to MESSAGE_OUT first, get out parity error message, then come\r
93                 // back here.\r
94         }\r
95         else if ((scsiDev.msgIn == MSG_LINKED_COMMAND_COMPLETE) ||\r
96                 (scsiDev.msgIn == MSG_LINKED_COMMAND_COMPLETE_WITH_FLAG))\r
97         {\r
98                 // Go back to the command phase and start again.\r
99                 scsiDev.phase = COMMAND;\r
100                 scsiDev.parityError = 0;\r
101                 scsiDev.dataPtr = 0;\r
102                 scsiDev.savedDataPtr = 0;\r
103                 scsiDev.dataLen = 0;\r
104                 scsiDev.status = GOOD;\r
105                 transfer.blocks = 0;\r
106                 transfer.currentBlock = 0;\r
107         }\r
108         else /*if (scsiDev.msgIn == MSG_COMMAND_COMPLETE)*/\r
109         {\r
110                 enter_BusFree();\r
111         }\r
112 }\r
113 \r
114 static void messageReject()\r
115 {\r
116         scsiEnterPhase(MESSAGE_IN);\r
117         scsiWriteByte(MSG_REJECT);\r
118 }\r
119 \r
120 static void enter_Status(uint8_t status)\r
121 {\r
122         scsiDev.status = status;\r
123         scsiDev.phase = STATUS;\r
124 \r
125         scsiDev.lastStatus = scsiDev.status;\r
126         scsiDev.lastSense = scsiDev.target->sense.code;\r
127         scsiDev.lastSenseASC = scsiDev.target->sense.asc;\r
128 }\r
129 \r
130 void process_Status()\r
131 {\r
132         scsiEnterPhase(STATUS);\r
133 \r
134         uint8_t message;\r
135 \r
136         uint8_t control = scsiDev.cdb[scsiDev.cdbLen - 1];\r
137         if ((scsiDev.status == GOOD) && (control & 0x01))\r
138         {\r
139                 // Linked command.\r
140                 scsiDev.status = INTERMEDIATE;\r
141                 if (control & 0x02)\r
142                 {\r
143                         message = MSG_LINKED_COMMAND_COMPLETE_WITH_FLAG;\r
144                 }\r
145                 else\r
146                 {\r
147                         message = MSG_LINKED_COMMAND_COMPLETE;\r
148                 }\r
149         }\r
150         else\r
151         {\r
152                 message = MSG_COMMAND_COMPLETE;\r
153         }\r
154         scsiWriteByte(scsiDev.status);\r
155 \r
156         scsiDev.lastStatus = scsiDev.status;\r
157         scsiDev.lastSense = scsiDev.target->sense.code;\r
158         scsiDev.lastSenseASC = scsiDev.target->sense.asc;\r
159 \r
160         // Command Complete occurs AFTER a valid status has been\r
161         // sent. then we go bus-free.\r
162         enter_MessageIn(message);\r
163 }\r
164 \r
165 static void enter_DataIn(int len)\r
166 {\r
167         scsiDev.dataLen = len;\r
168         scsiDev.phase = DATA_IN;\r
169 }\r
170 \r
171 static void process_DataIn()\r
172 {\r
173         uint32_t len;\r
174 \r
175         if (scsiDev.dataLen > sizeof(scsiDev.data))\r
176         {\r
177                 scsiDev.dataLen = sizeof(scsiDev.data);\r
178         }\r
179 \r
180         len = scsiDev.dataLen - scsiDev.dataPtr;\r
181         if (len > 0)\r
182         {\r
183                 scsiEnterPhase(DATA_IN);\r
184                 scsiWrite(scsiDev.data + scsiDev.dataPtr, len);\r
185                 scsiDev.dataPtr += len;\r
186         }\r
187 \r
188         if ((scsiDev.dataPtr >= scsiDev.dataLen) &&\r
189                 (transfer.currentBlock == transfer.blocks))\r
190         {\r
191                 enter_Status(GOOD);\r
192         }\r
193 }\r
194 \r
195 static void process_DataOut()\r
196 {\r
197         uint32_t len;\r
198 \r
199         if (scsiDev.dataLen > sizeof(scsiDev.data))\r
200         {\r
201                 scsiDev.dataLen = sizeof(scsiDev.data);\r
202         }\r
203 \r
204         scsiDev.parityError = 0;\r
205         len = scsiDev.dataLen - scsiDev.dataPtr;\r
206         if (len > 0)\r
207         {\r
208                 scsiEnterPhase(DATA_OUT);\r
209 \r
210                 scsiRead(scsiDev.data + scsiDev.dataPtr, len);\r
211                 scsiDev.dataPtr += len;\r
212 \r
213                 if (scsiDev.parityError &&\r
214                         (scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY) &&\r
215                         (scsiDev.compatMode >= COMPAT_SCSI2))\r
216                 {\r
217                         scsiDev.target->sense.code = ABORTED_COMMAND;\r
218                         scsiDev.target->sense.asc = SCSI_PARITY_ERROR;\r
219                         enter_Status(CHECK_CONDITION);\r
220                 }\r
221         }\r
222 \r
223         if ((scsiDev.dataPtr >= scsiDev.dataLen) &&\r
224                 (transfer.currentBlock == transfer.blocks))\r
225         {\r
226                 if (scsiDev.postDataOutHook != NULL)\r
227                 {\r
228                         scsiDev.postDataOutHook();\r
229                 }\r
230                 else\r
231                 {\r
232                         enter_Status(GOOD);\r
233                 }\r
234         }\r
235 }\r
236 \r
237 static const uint8_t CmdGroupBytes[8] = {6, 10, 10, 6, 6, 12, 6, 6};\r
238 static void process_Command()\r
239 {\r
240         int group;\r
241         uint8_t command;\r
242         uint8_t control;\r
243 \r
244         scsiEnterPhase(COMMAND);\r
245         scsiDev.parityError = 0;\r
246 \r
247         memset(scsiDev.cdb + 6, 0, sizeof(scsiDev.cdb) - 6);\r
248         scsiRead(scsiDev.cdb, 6);\r
249 \r
250         group = scsiDev.cdb[0] >> 5;\r
251         scsiDev.cdbLen = CmdGroupBytes[group];\r
252         if (scsiDev.cdbLen - 6 > 0)\r
253         {\r
254                 scsiRead(scsiDev.cdb + 6, scsiDev.cdbLen - 6);\r
255         }\r
256 \r
257         command = scsiDev.cdb[0];\r
258 \r
259         // Prefer LUN's set by IDENTIFY messages for newer hosts.\r
260         if (scsiDev.lun < 0)\r
261         {\r
262                 scsiDev.lun = scsiDev.cdb[1] >> 5;\r
263         }\r
264 \r
265         control = scsiDev.cdb[scsiDev.cdbLen - 1];\r
266 \r
267         scsiDev.cmdCount++;\r
268         const S2S_TargetCfg* cfg = scsiDev.target->cfg;\r
269 \r
270         if (unlikely(scsiDev.resetFlag))\r
271         {\r
272                 // Don't log bogus commands\r
273                 scsiDev.cmdCount--;\r
274                 memset(scsiDev.cdb, 0xff, sizeof(scsiDev.cdb));\r
275                 return;\r
276         }\r
277         else if (scsiDev.parityError &&\r
278                 (scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY) &&\r
279                 (scsiDev.compatMode >= COMPAT_SCSI2))\r
280         {\r
281                 scsiDev.target->sense.code = ABORTED_COMMAND;\r
282                 scsiDev.target->sense.asc = SCSI_PARITY_ERROR;\r
283                 enter_Status(CHECK_CONDITION);\r
284         }\r
285         else if ((control & 0x02) && ((control & 0x01) == 0))\r
286         {\r
287                 // FLAG set without LINK flag.\r
288                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
289                 scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
290                 enter_Status(CHECK_CONDITION);\r
291         }\r
292         else if (command == 0x12)\r
293         {\r
294                 s2s_scsiInquiry();\r
295         }\r
296         else if (command == 0x03)\r
297         {\r
298                 // REQUEST SENSE\r
299                 uint32_t allocLength = scsiDev.cdb[4];\r
300 \r
301                 // As specified by the SASI and SCSI1 standard.\r
302                 // Newer initiators won't be specifying 0 anyway.\r
303                 if (allocLength == 0) allocLength = 4;\r
304 \r
305                 memset(scsiDev.data, 0, 256); // Max possible alloc length\r
306                 scsiDev.data[0] = 0xF0;\r
307                 scsiDev.data[2] = scsiDev.target->sense.code & 0x0F;\r
308 \r
309                 scsiDev.data[3] = transfer.lba >> 24;\r
310                 scsiDev.data[4] = transfer.lba >> 16;\r
311                 scsiDev.data[5] = transfer.lba >> 8;\r
312                 scsiDev.data[6] = transfer.lba;\r
313 \r
314                 // Additional bytes if there are errors to report\r
315                 scsiDev.data[7] = 10; // additional length\r
316                 scsiDev.data[12] = scsiDev.target->sense.asc >> 8;\r
317                 scsiDev.data[13] = scsiDev.target->sense.asc;\r
318 \r
319                 // Silently truncate results. SCSI-2 spec 8.2.14.\r
320                 enter_DataIn(allocLength);\r
321 \r
322                 // This is a good time to clear out old sense information.\r
323                 scsiDev.target->sense.code = NO_SENSE;\r
324                 scsiDev.target->sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;\r
325         }\r
326         // Some old SCSI drivers do NOT properly support\r
327         // unitAttention. eg. the Mac Plus would trigger a SCSI reset\r
328         // on receiving the unit attention response on boot, thus\r
329         // triggering another unit attention condition.\r
330         else if (scsiDev.target->unitAttention &&\r
331                 (scsiDev.boardCfg.flags & S2S_CFG_ENABLE_UNIT_ATTENTION))\r
332         {\r
333                 scsiDev.target->sense.code = UNIT_ATTENTION;\r
334                 scsiDev.target->sense.asc = scsiDev.target->unitAttention;\r
335 \r
336                 // If initiator doesn't do REQUEST SENSE for the next command, then\r
337                 // data is lost.\r
338                 scsiDev.target->unitAttention = 0;\r
339 \r
340                 enter_Status(CHECK_CONDITION);\r
341         }\r
342         else if (scsiDev.lun)\r
343         {\r
344                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
345                 scsiDev.target->sense.asc = LOGICAL_UNIT_NOT_SUPPORTED;\r
346                 enter_Status(CHECK_CONDITION);\r
347         }\r
348         else if (command == 0x17 || command == 0x16)\r
349         {\r
350                 doReserveRelease();\r
351         }\r
352         else if ((scsiDev.target->reservedId >= 0) &&\r
353                 (scsiDev.target->reservedId != scsiDev.initiatorId))\r
354         {\r
355                 enter_Status(CONFLICT);\r
356         }\r
357         // Handle odd device types first that may override basic read and\r
358         // write commands. Will fall-through to generic disk handling.\r
359         else if (((cfg->deviceType == S2S_CFG_OPTICAL) && scsiCDRomCommand()) ||\r
360                 ((cfg->deviceType == S2S_CFG_SEQUENTIAL) && scsiTapeCommand()) ||\r
361                 ((cfg->deviceType == S2S_CFG_MO) && scsiMOCommand()))\r
362         {\r
363                 // Already handled.\r
364         }\r
365         else if (scsiDiskCommand())\r
366         {\r
367                 // Already handled.\r
368                 // check for the performance-critical read/write\r
369                 // commands ASAP.\r
370         }\r
371         else if (command == 0x1C)\r
372         {\r
373                 scsiReceiveDiagnostic();\r
374         }\r
375         else if (command == 0x1D)\r
376         {\r
377                 scsiSendDiagnostic();\r
378         }\r
379         else if (command == 0x3B)\r
380         {\r
381                 scsiWriteBuffer();\r
382         }\r
383         else if (command == 0x3C)\r
384         {\r
385                 scsiReadBuffer();\r
386         }\r
387         else if (!scsiModeCommand())\r
388         {\r
389                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
390                 scsiDev.target->sense.asc = INVALID_COMMAND_OPERATION_CODE;\r
391                 enter_Status(CHECK_CONDITION);\r
392         }\r
393 \r
394         // Successful\r
395         if (scsiDev.phase == COMMAND) // No status set, and not in DATA_IN\r
396         {\r
397                 enter_Status(GOOD);\r
398         }\r
399 \r
400 }\r
401 \r
402 static void doReserveRelease()\r
403 {\r
404         int extentReservation = scsiDev.cdb[1] & 1;\r
405         int thirdPty = scsiDev.cdb[1] & 0x10;\r
406         int thirdPtyId = (scsiDev.cdb[1] >> 1) & 0x7;\r
407         uint8_t command = scsiDev.cdb[0];\r
408 \r
409         int canRelease =\r
410                 (!thirdPty && (scsiDev.initiatorId == scsiDev.target->reservedId)) ||\r
411                         (thirdPty &&\r
412                                 (scsiDev.target->reserverId == scsiDev.initiatorId) &&\r
413                                 (scsiDev.target->reservedId == thirdPtyId)\r
414                         );\r
415 \r
416         if (extentReservation)\r
417         {\r
418                 // Not supported.\r
419                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
420                 scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
421                 enter_Status(CHECK_CONDITION);\r
422         }\r
423         else if (command == 0x17) // release\r
424         {\r
425                 if ((scsiDev.target->reservedId < 0) || canRelease)\r
426                 {\r
427                         scsiDev.target->reservedId = -1;\r
428                         scsiDev.target->reserverId = -1;\r
429                 }\r
430                 else\r
431                 {\r
432                         enter_Status(CONFLICT);\r
433                 }\r
434         }\r
435         else // assume reserve.\r
436         {\r
437                 if ((scsiDev.target->reservedId < 0) || canRelease)\r
438                 {\r
439                         scsiDev.target->reserverId = scsiDev.initiatorId;\r
440                         if (thirdPty)\r
441                         {\r
442                                 scsiDev.target->reservedId = thirdPtyId;\r
443                         }\r
444                         else\r
445                         {\r
446                                 scsiDev.target->reservedId = scsiDev.initiatorId;\r
447                         }\r
448                 }\r
449                 else\r
450                 {\r
451                         // Already reserved by someone else!\r
452                         enter_Status(CONFLICT);\r
453                 }\r
454         }\r
455 }\r
456 \r
457 static void scsiReset()\r
458 {\r
459         scsiDev.rstCount++;\r
460         s2s_ledOff();\r
461 \r
462         scsiPhyReset();\r
463 \r
464         scsiDev.parityError = 0;\r
465         scsiDev.phase = BUS_FREE;\r
466         scsiDev.atnFlag = 0;\r
467         scsiDev.resetFlag = 0;\r
468         scsiDev.lun = -1;\r
469         scsiDev.compatMode = COMPAT_UNKNOWN;\r
470 \r
471         if (scsiDev.target)\r
472         {\r
473                 if (scsiDev.target->unitAttention != POWER_ON_RESET)\r
474                 {\r
475                         scsiDev.target->unitAttention = SCSI_BUS_RESET;\r
476                 }\r
477                 scsiDev.target->reservedId = -1;\r
478                 scsiDev.target->reserverId = -1;\r
479                 scsiDev.target->sense.code = NO_SENSE;\r
480                 scsiDev.target->sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;\r
481         }\r
482         scsiDev.target = NULL;\r
483 \r
484         for (int i = 0; i < S2S_MAX_TARGETS; ++i)\r
485         {\r
486                 scsiDev.target[i].syncOffset = 0;\r
487                 scsiDev.target[i].syncPeriod = 0;\r
488         }\r
489 \r
490         scsiDiskReset();\r
491 \r
492         scsiDev.postDataOutHook = NULL;\r
493 \r
494         // Sleep to allow the bus to settle down a bit.\r
495         // We must be ready again within the "Reset to selection time" of\r
496         // 250ms.\r
497         // There is no guarantee that the RST line will be negated by then.\r
498         // NOTE: We could be connected and powered by USB for configuration,\r
499         // in which case TERMPWR cannot be supplied, and reset will ALWAYS\r
500         // be true. Therefore, the sleep here must be slow to avoid slowing\r
501         // USB comms\r
502         s2s_delay_ms(1); // 1ms.\r
503 }\r
504 \r
505 static void enter_SelectionPhase()\r
506 {\r
507         // Ignore stale versions of this flag, but ensure we know the\r
508         // current value if the flag is still set.\r
509         scsiDev.atnFlag = 0;\r
510         scsiDev.parityError = 0;\r
511         scsiDev.dataPtr = 0;\r
512         scsiDev.savedDataPtr = 0;\r
513         scsiDev.dataLen = 0;\r
514         scsiDev.status = GOOD;\r
515         scsiDev.phase = SELECTION;\r
516         scsiDev.lun = -1;\r
517         scsiDev.discPriv = 0;\r
518 \r
519         scsiDev.initiatorId = -1;\r
520         scsiDev.target = NULL;\r
521 \r
522         transfer.blocks = 0;\r
523         transfer.currentBlock = 0;\r
524 \r
525         scsiDev.postDataOutHook = NULL;\r
526 \r
527         scsiDev.needSyncNegotiationAck = 0;\r
528 }\r
529 \r
530 static void process_SelectionPhase()\r
531 {\r
532         // Selection delays.\r
533         // Many SCSI1 samplers that use a 5380 chip need a delay of at least 1ms.\r
534         // The Mac Plus boot-time (ie. rom code) selection abort time\r
535         // is < 1ms and must have no delay (standard suggests 250ms abort time)\r
536         // Most newer SCSI2 hosts don't care either way.\r
537         if (scsiDev.boardCfg.selectionDelay == 255) // auto\r
538         {\r
539                 if (scsiDev.compatMode < COMPAT_SCSI2)\r
540                 {\r
541                         s2s_delay_ms(1);\r
542                 }\r
543         }\r
544         else if (scsiDev.boardCfg.selectionDelay != 0)\r
545         {\r
546                 s2s_delay_ms(scsiDev.boardCfg.selectionDelay);\r
547         }\r
548 \r
549         uint8_t selStatus = *SCSI_STS_SELECTED;\r
550 \r
551         int tgtIndex;\r
552         TargetState* target = NULL;\r
553         for (tgtIndex = 0; tgtIndex < S2S_MAX_TARGETS; ++tgtIndex)\r
554         {\r
555                 if (scsiDev.targets[tgtIndex].targetId == (selStatus & 7))\r
556                 {\r
557                         target = &scsiDev.targets[tgtIndex];\r
558                         break;\r
559                 }\r
560         }\r
561         if ((target != NULL) && (selStatus & 0x40))\r
562 // TODO         (goodParity || !(scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY) || !atnFlag)\r
563         {\r
564                 // We've been selected!\r
565                 // Assert BSY - Selection success!\r
566                 // must happen within 200us (Selection abort time) of seeing our\r
567                 // ID + SEL.\r
568                 // (Note: the initiator will be waiting the "Selection time-out delay"\r
569                 // for our BSY response, which is actually a very generous 250ms)\r
570                 *SCSI_CTRL_BSY = 1;\r
571                 s2s_ledOn();\r
572 \r
573                 scsiDev.target = target;\r
574 \r
575                 // Do we enter MESSAGE OUT immediately ? SCSI 1 and 2 standards says\r
576                 // move to MESSAGE OUT if ATN is true before we assert BSY.\r
577                 // The initiator should assert ATN with SEL.\r
578                 scsiDev.atnFlag = selStatus & 0x80;\r
579 \r
580 \r
581                 // Unit attention breaks many older SCSI hosts. Disable it completely\r
582                 // for SCSI-1 (and older) hosts, regardless of our configured setting.\r
583                 // Enable the compatability mode also as many SASI and SCSI1\r
584                 // controllers don't generate parity bits.\r
585                 if (!scsiDev.atnFlag)\r
586                 {\r
587                         target->unitAttention = 0;\r
588                         scsiDev.compatMode = COMPAT_SCSI1;\r
589                 }\r
590                 else if (!(scsiDev.boardCfg.flags & S2S_CFG_ENABLE_SCSI2))\r
591                 {\r
592                         scsiDev.compatMode = COMPAT_SCSI1;\r
593                 }\r
594                 else if (scsiDev.compatMode == COMPAT_UNKNOWN)\r
595                 {\r
596                         scsiDev.compatMode = COMPAT_SCSI2;\r
597                 }\r
598 \r
599                 scsiDev.selCount++;\r
600 \r
601 \r
602                 // Save our initiator now that we're no longer in a time-critical\r
603                 // section.\r
604                 // SCSI1/SASI initiators may not set their own ID.\r
605                 scsiDev.initiatorId = (selStatus >> 3) & 0x7;\r
606 \r
607                 while (likely(!scsiDev.resetFlag) && scsiStatusSEL())\r
608                 {\r
609                         // Wait until the end of the selection phase.\r
610                 }\r
611 \r
612                 scsiDev.phase = COMMAND;\r
613         }\r
614         else if (!selStatus)\r
615         {\r
616                 scsiDev.phase = BUS_BUSY;\r
617         }\r
618 }\r
619 \r
620 static void process_MessageOut()\r
621 {\r
622         scsiEnterPhase(MESSAGE_OUT);\r
623 \r
624         scsiDev.atnFlag = 0;\r
625         scsiDev.parityError = 0;\r
626         scsiDev.msgOut = scsiReadByte();\r
627         scsiDev.msgCount++;\r
628 \r
629         if (scsiDev.parityError &&\r
630                 (scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY) &&\r
631                 (scsiDev.compatMode >= COMPAT_SCSI2))\r
632         {\r
633                 // Skip the remaining message bytes, and then start the MESSAGE_OUT\r
634                 // phase again from the start. The initiator will re-send the\r
635                 // same set of messages.\r
636                 while (scsiStatusATN() && !scsiDev.resetFlag)\r
637                 {\r
638                         scsiReadByte();\r
639                 }\r
640 \r
641                 // Go-back and try the message again.\r
642                 scsiDev.atnFlag = 1;\r
643                 scsiDev.parityError = 0;\r
644         }\r
645         else if (scsiDev.msgOut == 0x00)\r
646         {\r
647                 // COMMAND COMPLETE. but why would the target be receiving this ? nfi.\r
648                 enter_BusFree();\r
649         }\r
650         else if (scsiDev.msgOut == 0x06)\r
651         {\r
652                 // ABORT\r
653                 scsiDiskReset();\r
654                 enter_BusFree();\r
655         }\r
656         else if (scsiDev.msgOut == 0x0C)\r
657         {\r
658                 // BUS DEVICE RESET\r
659 \r
660                 scsiDiskReset();\r
661 \r
662                 scsiDev.target->unitAttention = SCSI_BUS_RESET;\r
663 \r
664                 // ANY initiator can reset the reservation state via this message.\r
665                 scsiDev.target->reservedId = -1;\r
666                 scsiDev.target->reserverId = -1;\r
667 \r
668                 // Cancel any sync negotiation\r
669                 scsiDev.target->syncOffset = 0;\r
670                 scsiDev.target->syncPeriod = 0;\r
671 \r
672                 enter_BusFree();\r
673         }\r
674         else if (scsiDev.msgOut == 0x05)\r
675         {\r
676                 // Initiate Detected Error\r
677                 // Ignore for now\r
678         }\r
679         else if (scsiDev.msgOut == 0x0F)\r
680         {\r
681                 // INITIATE RECOVERY\r
682                 // Ignore for now\r
683         }\r
684         else if (scsiDev.msgOut == 0x10)\r
685         {\r
686                 // RELEASE RECOVERY\r
687                 // Ignore for now\r
688                 enter_BusFree();\r
689         }\r
690         else if (scsiDev.msgOut == MSG_REJECT)\r
691         {\r
692                 // Message Reject\r
693                 // Oh well.\r
694 \r
695                 if (scsiDev.needSyncNegotiationAck)\r
696                 {\r
697                         scsiDev.target->syncOffset = 0;\r
698                         scsiDev.target->syncPeriod = 0;\r
699                         scsiDev.needSyncNegotiationAck = 0;\r
700                 }\r
701         }\r
702         else if (scsiDev.msgOut == 0x08)\r
703         {\r
704                 // NOP\r
705         }\r
706         else if (scsiDev.msgOut == 0x09)\r
707         {\r
708                 // Message Parity Error\r
709                 // Go back and re-send the last message.\r
710                 scsiDev.phase = MESSAGE_IN;\r
711         }\r
712         else if (scsiDev.msgOut & 0x80) // 0x80 -> 0xFF\r
713         {\r
714                 // IDENTIFY\r
715                 if ((scsiDev.msgOut & 0x18) || // Reserved bits set.\r
716                         (scsiDev.msgOut & 0x20))  // We don't have any target routines!\r
717                 {\r
718                         messageReject();\r
719                 }\r
720 \r
721                 scsiDev.lun = scsiDev.msgOut & 0x7;\r
722                 scsiDev.discPriv = \r
723                         ((scsiDev.msgOut & 0x40) && (scsiDev.initiatorId >= 0))\r
724                                 ? 1 : 0;\r
725         }\r
726         else if (scsiDev.msgOut >= 0x20 && scsiDev.msgOut <= 0x2F)\r
727         {\r
728                 // Two byte message. We don't support these. read and discard.\r
729                 scsiReadByte();\r
730 \r
731                 if (scsiDev.msgOut == 0x23) {\r
732                         // Ignore Wide Residue. We're only 8 bit anyway.\r
733                 } else {\r
734                         messageReject();\r
735                 }\r
736         }\r
737         else if (scsiDev.msgOut == 0x01)\r
738         {\r
739                 int i;\r
740 \r
741                 // Extended message.\r
742                 int msgLen = scsiReadByte();\r
743                 if (msgLen == 0) msgLen = 256;\r
744                 uint8_t extmsg[256];\r
745                 for (i = 0; i < msgLen && !scsiDev.resetFlag; ++i)\r
746                 {\r
747                         // Discard bytes.\r
748                         extmsg[i] = scsiReadByte();\r
749                 }\r
750 \r
751                 if (extmsg[0] == 3 && msgLen == 2) // Wide Data Request\r
752                 {\r
753                         // Negotiate down to 8bit\r
754                         scsiEnterPhase(MESSAGE_IN);\r
755                         static const uint8_t WDTR[] = {0x01, 0x02, 0x03, 0x00};\r
756                         scsiWrite(WDTR, sizeof(WDTR));\r
757                 }\r
758                 else if (extmsg[0] == 1 && msgLen == 3) // Synchronous data request\r
759                 {\r
760                         int transferPeriod = extmsg[1];\r
761                         int offset = extmsg[2];\r
762 \r
763                         if (transferPeriod > 50) // 200ns, 5MB/s\r
764                         {\r
765                                 scsiDev.target->syncOffset = 0;\r
766                                 scsiDev.target->syncPeriod = 0;\r
767                         } else {\r
768                                 scsiDev.target->syncOffset = offset < 15 ? offset : 15;\r
769                                 // FAST20 / 50ns / 20MHz is disabled for now due to\r
770                                 // data corruption while reading data. We can count the\r
771                                 // ACK's correctly, but can't save the data to a register\r
772                                 // before it changes.\r
773                                 // TODO work out the fastest sync period that will work\r
774                                 /*\r
775                                 if (transferPeriod <= 12)\r
776                                 {\r
777                                         scsiDev.target->syncPeriod = 12; // 50ns, 20MB/s\r
778                                 }\r
779                                 else */if (transferPeriod <= 25)\r
780                                 {\r
781                                         scsiDev.target->syncPeriod = 25; // 100ns, 10MB/s\r
782                                 } else {\r
783                                         scsiDev.target->syncPeriod = 50; // 200ns, 5MB/s\r
784                                 }\r
785                         }\r
786 \r
787                         scsiEnterPhase(MESSAGE_IN);\r
788                         uint8_t SDTR[] = {0x01, 0x03, 0x01, scsiDev.target->syncPeriod, scsiDev.target->syncOffset};\r
789                         scsiWrite(SDTR, sizeof(SDTR));\r
790                         scsiDev.needSyncNegotiationAck = 1; // Check if this message is rejected.\r
791                 }\r
792                 else\r
793                 {\r
794                         // Not supported\r
795                         messageReject();\r
796                 }\r
797         }\r
798         else\r
799         {\r
800                 messageReject();\r
801         }\r
802 \r
803         // Re-check the ATN flag in case it stays asserted.\r
804         scsiDev.atnFlag |= scsiStatusATN();\r
805 \r
806         if (!scsiDev.atnFlag)\r
807         {\r
808                 // Message wasn't rejected!\r
809                 scsiDev.needSyncNegotiationAck = 0;\r
810         }\r
811 }\r
812 \r
813 void scsiPoll(void)\r
814 {\r
815         if (unlikely(scsiDev.resetFlag))\r
816         {\r
817                 scsiReset();\r
818                 if ((scsiDev.resetFlag = scsiStatusRST()))\r
819                 {\r
820                         // Still in reset phase. Do not try and process any commands.\r
821                         return;\r
822                 }\r
823         }\r
824 \r
825         switch (scsiDev.phase)\r
826         {\r
827         case BUS_FREE:\r
828                 if (scsiStatusBSY())\r
829                 {\r
830                         scsiDev.phase = BUS_BUSY;\r
831                 }\r
832                 // The Arbitration phase is optional for SCSI1/SASI hosts if there is only\r
833                 // one initiator in the chain. Support this by moving\r
834                 // straight to selection if SEL is asserted.\r
835                 // ie. the initiator won't assert BSY and it's own ID before moving to selection.\r
836                 else if (*SCSI_STS_SELECTED)\r
837                 {\r
838                         enter_SelectionPhase();\r
839                 }\r
840         break;\r
841 \r
842         case BUS_BUSY:\r
843                 // Someone is using the bus. Perhaps they are trying to\r
844                 // select us.\r
845                 if (*SCSI_STS_SELECTED)\r
846                 {\r
847                         enter_SelectionPhase();\r
848                 }\r
849                 else if (!scsiStatusBSY())\r
850                 {\r
851                         scsiDev.phase = BUS_FREE;\r
852                 }\r
853         break;\r
854 \r
855         case ARBITRATION:\r
856                 // TODO Support reselection.\r
857                 break;\r
858 \r
859         case SELECTION:\r
860                 process_SelectionPhase();\r
861         break;\r
862 \r
863         case RESELECTION:\r
864                 // Not currently supported!\r
865         break;\r
866 \r
867         case COMMAND:\r
868                 // Do not check ATN here. SCSI 1 & 2 initiators must set ATN\r
869                 // and SEL together upon entering the selection phase if they\r
870                 // want to send a message (IDENTIFY) immediately.\r
871                 if (scsiDev.atnFlag)\r
872                 {\r
873                         process_MessageOut();\r
874                 }\r
875                 else\r
876                 {\r
877                         process_Command();\r
878                 }\r
879         break;\r
880 \r
881         case DATA_IN:\r
882                 scsiDev.atnFlag |= scsiStatusATN();\r
883                 if (scsiDev.atnFlag)\r
884                 {\r
885                         process_MessageOut();\r
886                 }\r
887                 else\r
888                 {\r
889                         process_DataIn();\r
890                 }\r
891         break;\r
892 \r
893         case DATA_OUT:\r
894                 scsiDev.atnFlag |= scsiStatusATN();\r
895                 if (scsiDev.atnFlag)\r
896                 {\r
897                         process_MessageOut();\r
898                 }\r
899                 else\r
900                 {\r
901                         process_DataOut();\r
902                 }\r
903         break;\r
904 \r
905         case STATUS:\r
906                 scsiDev.atnFlag |= scsiStatusATN();\r
907                 if (scsiDev.atnFlag)\r
908                 {\r
909                         process_MessageOut();\r
910                 }\r
911                 else\r
912                 {\r
913                         process_Status();\r
914                 }\r
915         break;\r
916 \r
917         case MESSAGE_IN:\r
918                 scsiDev.atnFlag |= scsiStatusATN();\r
919                 if (scsiDev.atnFlag)\r
920                 {\r
921                         process_MessageOut();\r
922                 }\r
923                 else\r
924                 {\r
925                         process_MessageIn();\r
926                 }\r
927 \r
928         break;\r
929 \r
930         case MESSAGE_OUT:\r
931                 process_MessageOut();\r
932         break;\r
933         }\r
934 }\r
935 \r
936 void scsiInit()\r
937 {\r
938         static int firstInit = 1;\r
939 \r
940         scsiDev.atnFlag = 0;\r
941         scsiDev.resetFlag = 1;\r
942         scsiDev.phase = BUS_FREE;\r
943         scsiDev.target = NULL;\r
944         scsiDev.compatMode = COMPAT_UNKNOWN;\r
945 \r
946         int i;\r
947         for (i = 0; i < S2S_MAX_TARGETS; ++i)\r
948         {\r
949                 const S2S_TargetCfg* cfg = s2s_getConfigByIndex(i);\r
950                 if (cfg && (cfg->scsiId & S2S_CFG_TARGET_ENABLED))\r
951                 {\r
952                         scsiDev.targets[i].targetId = cfg->scsiId & S2S_CFG_TARGET_ID_BITS;\r
953                         scsiDev.targets[i].cfg = cfg;\r
954 \r
955                         scsiDev.targets[i].liveCfg.bytesPerSector = cfg->bytesPerSector;\r
956                 }\r
957                 else\r
958                 {\r
959                         scsiDev.targets[i].targetId = 0xff;\r
960                         scsiDev.targets[i].cfg = NULL;\r
961                 }\r
962                 scsiDev.targets[i].reservedId = -1;\r
963                 scsiDev.targets[i].reserverId = -1;\r
964                 if (firstInit)\r
965                 {\r
966                         scsiDev.targets[i].unitAttention = POWER_ON_RESET;\r
967                 }\r
968                 else\r
969                 {\r
970                         scsiDev.targets[i].unitAttention = PARAMETERS_CHANGED;\r
971                 }\r
972                 scsiDev.targets[i].sense.code = NO_SENSE;\r
973                 scsiDev.targets[i].sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;\r
974 \r
975                 scsiDev.targets[i].syncOffset = 0;\r
976                 scsiDev.targets[i].syncPeriod = 0;\r
977         }\r
978         firstInit = 0;\r
979 }\r
980 \r
981 /* TODO REENABLE\r
982 void scsiDisconnect()\r
983 {\r
984         scsiEnterPhase(MESSAGE_IN);\r
985         scsiWriteByte(0x02); // save data pointer\r
986         scsiWriteByte(0x04); // disconnect msg.\r
987 \r
988         // For now, the caller is responsible for tracking the disconnected\r
989         // state, and calling scsiReconnect.\r
990         // Ideally the client would exit their loop and we'd implement this\r
991         // as part of scsiPoll\r
992         int phase = scsiDev.phase;\r
993         enter_BusFree();\r
994         scsiDev.phase = phase;\r
995 }\r
996 */\r
997 \r
998 /* TODO REENABLE\r
999 int scsiReconnect()\r
1000 {\r
1001         int reconnected = 0;\r
1002 \r
1003         int sel = SCSI_ReadFilt(SCSI_Filt_SEL);\r
1004         int bsy = SCSI_ReadFilt(SCSI_Filt_BSY);\r
1005         if (!sel && !bsy)\r
1006         {\r
1007                 s2s_delay_us(1);\r
1008                 sel = SCSI_ReadFilt(SCSI_Filt_SEL);\r
1009                 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);\r
1010         }\r
1011 \r
1012         if (!sel && !bsy)\r
1013         {\r
1014                 // Arbitrate.\r
1015                 s2s_ledOn();\r
1016                 uint8_t scsiIdMask = 1 << scsiDev.target->targetId;\r
1017                 SCSI_Out_Bits_Write(scsiIdMask);\r
1018                 SCSI_Out_Ctl_Write(1); // Write bits manually.\r
1019                 SCSI_SetPin(SCSI_Out_BSY);\r
1020 \r
1021                 s2s_delay_us(3); // arbitrate delay. 2.4us.\r
1022 \r
1023                 uint8_t dbx = scsiReadDBxPins();\r
1024                 sel = SCSI_ReadFilt(SCSI_Filt_SEL);\r
1025                 if (sel || ((dbx ^ scsiIdMask) > scsiIdMask))\r
1026                 {\r
1027                         // Lost arbitration.\r
1028                         SCSI_Out_Ctl_Write(0);\r
1029                         SCSI_ClearPin(SCSI_Out_BSY);\r
1030                         s2s_ledOff();\r
1031                 }\r
1032                 else\r
1033                 {\r
1034                         // Won arbitration\r
1035                         SCSI_SetPin(SCSI_Out_SEL);\r
1036                         s2s_delay_us(1); // Bus clear + Bus settle.\r
1037 \r
1038                         // Reselection phase\r
1039                         SCSI_CTL_PHASE_Write(__scsiphase_io);\r
1040                         SCSI_Out_Bits_Write(scsiIdMask | (1 << scsiDev.initiatorId));\r
1041                         scsiDeskewDelay(); // 2 deskew delays\r
1042                         scsiDeskewDelay(); // 2 deskew delays\r
1043                         SCSI_ClearPin(SCSI_Out_BSY);\r
1044                         s2s_delay_us(1);  // Bus Settle Delay\r
1045 \r
1046                         uint32_t waitStart_ms = getTime_ms();\r
1047                         bsy = SCSI_ReadFilt(SCSI_Filt_BSY);\r
1048                         // Wait for initiator.\r
1049                         while (\r
1050                                 !bsy &&\r
1051                                 !scsiDev.resetFlag &&\r
1052                                 (elapsedTime_ms(waitStart_ms) < 250))\r
1053                         {\r
1054                                 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);\r
1055                         }\r
1056 \r
1057                         if (bsy)\r
1058                         {\r
1059                                 SCSI_SetPin(SCSI_Out_BSY);\r
1060                                 scsiDeskewDelay(); // 2 deskew delays\r
1061                                 scsiDeskewDelay(); // 2 deskew delays\r
1062                                 SCSI_ClearPin(SCSI_Out_SEL);\r
1063 \r
1064                                 // Prepare for the initial IDENTIFY message.\r
1065                                 SCSI_Out_Ctl_Write(0);\r
1066                                 scsiEnterPhase(MESSAGE_IN);\r
1067 \r
1068                                 // Send identify command\r
1069                                 scsiWriteByte(0x80);\r
1070 \r
1071                                 scsiEnterPhase(scsiDev.phase);\r
1072                                 reconnected = 1;\r
1073                         }\r
1074                         else\r
1075                         {\r
1076                                 // reselect timeout.\r
1077                                 SCSI_Out_Ctl_Write(0);\r
1078                                 SCSI_ClearPin(SCSI_Out_SEL);\r
1079                                 SCSI_CTL_PHASE_Write(0);\r
1080                                 s2s_ledOff();\r
1081                         }\r
1082                 }\r
1083         }\r
1084         return reconnected;\r
1085 }\r
1086 */\r
1087 \r