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