15622d5969234640c27bbf515aecc9a79b6d0e52
[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)
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)
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)
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
463 if (scsiDev.target)
464 {
465 if (scsiDev.target->unitAttention != POWER_ON_RESET)
466 {
467 scsiDev.target->unitAttention = SCSI_BUS_RESET;
468 }
469 scsiDev.target->reservedId = -1;
470 scsiDev.target->reserverId = -1;
471 scsiDev.target->sense.code = NO_SENSE;
472 scsiDev.target->sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;
473 }
474 scsiDev.target = NULL;
475 scsiDiskReset();
476
477 scsiDev.postDataOutHook = NULL;
478
479 // Sleep to allow the bus to settle down a bit.
480 // We must be ready again within the "Reset to selection time" of
481 // 250ms.
482 // There is no guarantee that the RST line will be negated by then.
483 // NOTE: We could be connected and powered by USB for configuration,
484 // in which case TERMPWR cannot be supplied, and reset will ALWAYS
485 // be true. Therefore, the sleep here must be slow to avoid slowing
486 // USB comms
487 CyDelay(1); // 1ms.
488 }
489
490 static void enter_SelectionPhase()
491 {
492 // Ignore stale versions of this flag, but ensure we know the
493 // current value if the flag is still set.
494 scsiDev.atnFlag = 0;
495 scsiDev.parityError = 0;
496 scsiDev.dataPtr = 0;
497 scsiDev.savedDataPtr = 0;
498 scsiDev.dataLen = 0;
499 scsiDev.status = GOOD;
500 scsiDev.phase = SELECTION;
501 scsiDev.lun = -1;
502 scsiDev.discPriv = 0;
503 scsiDev.compatMode = 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 int sel = SCSI_ReadFilt(SCSI_Filt_SEL);
517 int bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
518
519 // Only read these pins AFTER SEL and BSY - we don't want to catch them
520 // during a transition period.
521 uint8 mask = scsiReadDBxPins();
522 int maskBitCount = countBits(mask);
523 int goodParity = (Lookup_OddParity[mask] == SCSI_ReadPin(SCSI_In_DBP));
524 int atnFlag = SCSI_ReadFilt(SCSI_Filt_ATN);
525
526 int tgtIndex;
527 TargetState* target = NULL;
528 for (tgtIndex = 0; tgtIndex < MAX_SCSI_TARGETS; ++tgtIndex)
529 {
530 if (mask & (1 << scsiDev.targets[tgtIndex].targetId))
531 {
532 target = &scsiDev.targets[tgtIndex];
533 break;
534 }
535 }
536 if (!bsy && sel &&
537 target &&
538 (goodParity || !(target->cfg->flags & CONFIG_ENABLE_PARITY) || !atnFlag) &&
539 likely(maskBitCount <= 2))
540 {
541 scsiDev.target = target;
542
543 // Do we enter MESSAGE OUT immediately ? SCSI 1 and 2 standards says
544 // move to MESSAGE OUT if ATN is true before we assert BSY.
545 // The initiator should assert ATN with SEL.
546 scsiDev.atnFlag = atnFlag;
547
548 // Unit attention breaks many older SCSI hosts. Disable it completely
549 // for SCSI-1 (and older) hosts, regardless of our configured setting.
550 // Enable the compatability mode also as many SASI and SCSI1
551 // controllers don't generate parity bits.
552 if (!scsiDev.atnFlag)
553 {
554 target->unitAttention = 0;
555 scsiDev.compatMode = 1;
556 }
557
558 // We've been selected!
559 // Assert BSY - Selection success!
560 // must happen within 200us (Selection abort time) of seeing our
561 // ID + SEL.
562 // (Note: the initiator will be waiting the "Selection time-out delay"
563 // for our BSY response, which is actually a very generous 250ms)
564 SCSI_SetPin(SCSI_Out_BSY);
565 ledOn();
566
567 scsiDev.selCount++;
568
569 // Wait until the end of the selection phase.
570 while (likely(!scsiDev.resetFlag))
571 {
572 if (!SCSI_ReadFilt(SCSI_Filt_SEL))
573 {
574 break;
575 }
576 }
577
578 // Save our initiator now that we're no longer in a time-critical
579 // section.
580 // SCSI1/SASI initiators may not set their own ID.
581 {
582 int i;
583 uint8_t initiatorMask = mask ^ (1 << target->targetId);
584 scsiDev.initiatorId = -1;
585 for (i = 0; i < 8; ++i)
586 {
587 if (initiatorMask & (1 << i))
588 {
589 scsiDev.initiatorId = i;
590 break;
591 }
592 }
593 }
594
595 scsiDev.phase = COMMAND;
596 }
597 else if (!sel)
598 {
599 scsiDev.phase = BUS_BUSY;
600 }
601 }
602
603 static void process_MessageOut()
604 {
605 scsiEnterPhase(MESSAGE_OUT);
606
607 scsiDev.atnFlag = 0;
608 scsiDev.parityError = 0;
609 scsiDev.msgOut = scsiReadByte();
610 scsiDev.msgCount++;
611
612 if (scsiDev.parityError &&
613 (scsiDev.target->cfg->flags & CONFIG_ENABLE_PARITY) &&
614 !scsiDev.compatMode)
615 {
616 // Skip the remaining message bytes, and then start the MESSAGE_OUT
617 // phase again from the start. The initiator will re-send the
618 // same set of messages.
619 while (SCSI_ReadFilt(SCSI_Filt_ATN) && !scsiDev.resetFlag)
620 {
621 scsiReadByte();
622 }
623
624 // Go-back and try the message again.
625 scsiDev.atnFlag = 1;
626 scsiDev.parityError = 0;
627 }
628 else if (scsiDev.msgOut == 0x00)
629 {
630 // COMMAND COMPLETE. but why would the target be receiving this ? nfi.
631 enter_BusFree();
632 }
633 else if (scsiDev.msgOut == 0x06)
634 {
635 // ABORT
636 scsiDiskReset();
637 enter_BusFree();
638 }
639 else if (scsiDev.msgOut == 0x0C)
640 {
641 // BUS DEVICE RESET
642
643 scsiDiskReset();
644
645 scsiDev.target->unitAttention = SCSI_BUS_RESET;
646
647 // ANY initiator can reset the reservation state via this message.
648 scsiDev.target->reservedId = -1;
649 scsiDev.target->reserverId = -1;
650 enter_BusFree();
651 }
652 else if (scsiDev.msgOut == 0x05)
653 {
654 // Initiate Detected Error
655 // Ignore for now
656 }
657 else if (scsiDev.msgOut == 0x0F)
658 {
659 // INITIATE RECOVERY
660 // Ignore for now
661 }
662 else if (scsiDev.msgOut == 0x10)
663 {
664 // RELEASE RECOVERY
665 // Ignore for now
666 enter_BusFree();
667 }
668 else if (scsiDev.msgOut == MSG_REJECT)
669 {
670 // Message Reject
671 // Oh well.
672 scsiDev.resetFlag = 1;
673 }
674 else if (scsiDev.msgOut == 0x08)
675 {
676 // NOP
677 }
678 else if (scsiDev.msgOut == 0x09)
679 {
680 // Message Parity Error
681 // Go back and re-send the last message.
682 scsiDev.phase = MESSAGE_IN;
683 }
684 else if (scsiDev.msgOut & 0x80) // 0x80 -> 0xFF
685 {
686 // IDENTIFY
687 if ((scsiDev.msgOut & 0x18) || // Reserved bits set.
688 (scsiDev.msgOut & 0x20)) // We don't have any target routines!
689 {
690 messageReject();
691 }
692
693 scsiDev.lun = scsiDev.msgOut & 0x7;
694 scsiDev.discPriv =
695 ((scsiDev.msgOut & 0x40) && (scsiDev.initiatorId >= 0))
696 ? 1 : 0;
697 }
698 else if (scsiDev.msgOut >= 0x20 && scsiDev.msgOut <= 0x2F)
699 {
700 // Two byte message. We don't support these. read and discard.
701 scsiReadByte();
702 }
703 else if (scsiDev.msgOut == 0x01)
704 {
705 int i;
706
707 // Extended message.
708 int msgLen = scsiReadByte();
709 if (msgLen == 0) msgLen = 256;
710 uint8_t extmsg[256];
711 for (i = 0; i < msgLen && !scsiDev.resetFlag; ++i)
712 {
713 // Discard bytes.
714 extmsg[i] = scsiReadByte();
715 }
716
717 if (extmsg[0] == 3 && msgLen == 2) // Wide Data Request
718 {
719 // Negotiate down to 8bit
720 scsiEnterPhase(MESSAGE_IN);
721 static const uint8_t WDTR[] = {0x01, 0x02, 0x03, 0x00};
722 scsiWrite(WDTR, sizeof(WDTR));
723 }
724 else if (extmsg[0] == 1 && msgLen == 5) // Synchronous data request
725 {
726 // Negotiate back to async
727 scsiEnterPhase(MESSAGE_IN);
728 static const uint8_t SDTR[] = {0x01, 0x03, 0x01, 0x00, 0x00};
729 scsiWrite(SDTR, sizeof(SDTR));
730 }
731 else
732 {
733 // Not supported
734 messageReject();
735 }
736 }
737 else
738 {
739 messageReject();
740 }
741
742 // Re-check the ATN flag in case it stays asserted.
743 scsiDev.atnFlag |= SCSI_ReadFilt(SCSI_Filt_ATN);
744 }
745
746 void scsiPoll(void)
747 {
748 if (unlikely(scsiDev.resetFlag))
749 {
750 scsiReset();
751 if ((scsiDev.resetFlag = SCSI_ReadFilt(SCSI_Filt_RST)))
752 {
753 // Still in reset phase. Do not try and process any commands.
754 return;
755 }
756 }
757
758 switch (scsiDev.phase)
759 {
760 case BUS_FREE:
761 if (SCSI_ReadFilt(SCSI_Filt_BSY))
762 {
763 scsiDev.phase = BUS_BUSY;
764 }
765 // The Arbitration phase is optional for SCSI1/SASI hosts if there is only
766 // one initiator in the chain. Support this by moving
767 // straight to selection if SEL is asserted.
768 // ie. the initiator won't assert BSY and it's own ID before moving to selection.
769 else if (SCSI_ReadFilt(SCSI_Filt_SEL))
770 {
771 enter_SelectionPhase();
772 }
773 break;
774
775 case BUS_BUSY:
776 // Someone is using the bus. Perhaps they are trying to
777 // select us.
778 if (SCSI_ReadFilt(SCSI_Filt_SEL))
779 {
780 enter_SelectionPhase();
781 }
782 else if (!SCSI_ReadFilt(SCSI_Filt_BSY))
783 {
784 scsiDev.phase = BUS_FREE;
785 }
786 break;
787
788 case ARBITRATION:
789 // TODO Support reselection.
790 break;
791
792 case SELECTION:
793 process_SelectionPhase();
794 break;
795
796 case RESELECTION:
797 // Not currently supported!
798 break;
799
800 case COMMAND:
801 // Do not check ATN here. SCSI 1 & 2 initiators must set ATN
802 // and SEL together upon entering the selection phase if they
803 // want to send a message (IDENTIFY) immediately.
804 if (scsiDev.atnFlag)
805 {
806 process_MessageOut();
807 }
808 else
809 {
810 process_Command();
811 }
812 break;
813
814 case DATA_IN:
815 scsiDev.atnFlag |= SCSI_ReadFilt(SCSI_Filt_ATN);
816 if (scsiDev.atnFlag)
817 {
818 process_MessageOut();
819 }
820 else
821 {
822 process_DataIn();
823 }
824 break;
825
826 case DATA_OUT:
827 scsiDev.atnFlag |= SCSI_ReadFilt(SCSI_Filt_ATN);
828 if (scsiDev.atnFlag)
829 {
830 process_MessageOut();
831 }
832 else
833 {
834 process_DataOut();
835 }
836 break;
837
838 case STATUS:
839 scsiDev.atnFlag |= SCSI_ReadFilt(SCSI_Filt_ATN);
840 if (scsiDev.atnFlag)
841 {
842 process_MessageOut();
843 }
844 else
845 {
846 process_Status();
847 }
848 break;
849
850 case MESSAGE_IN:
851 scsiDev.atnFlag |= SCSI_ReadFilt(SCSI_Filt_ATN);
852 if (scsiDev.atnFlag)
853 {
854 process_MessageOut();
855 }
856 else
857 {
858 process_MessageIn();
859 }
860
861 break;
862
863 case MESSAGE_OUT:
864 process_MessageOut();
865 break;
866 }
867 }
868
869 void scsiInit()
870 {
871 scsiDev.atnFlag = 0;
872 scsiDev.resetFlag = 1;
873 scsiDev.phase = BUS_FREE;
874 scsiDev.target = NULL;
875
876 int i;
877 for (i = 0; i < MAX_SCSI_TARGETS; ++i)
878 {
879 const TargetConfig* cfg = getConfigByIndex(i);
880 if (cfg && (cfg->scsiId & CONFIG_TARGET_ENABLED))
881 {
882 scsiDev.targets[i].targetId = cfg->scsiId & CONFIG_TARGET_ID_BITS;
883 scsiDev.targets[i].cfg = cfg;
884
885 scsiDev.targets[i].liveCfg.bytesPerSector = cfg->bytesPerSector;
886 }
887 else
888 {
889 scsiDev.targets[i].targetId = 0xff;
890 scsiDev.targets[i].cfg = NULL;
891 }
892 scsiDev.targets[i].reservedId = -1;
893 scsiDev.targets[i].reserverId = -1;
894 scsiDev.targets[i].unitAttention = POWER_ON_RESET;
895 scsiDev.targets[i].sense.code = NO_SENSE;
896 scsiDev.targets[i].sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;
897 }
898 }
899
900 void scsiDisconnect()
901 {
902 scsiEnterPhase(MESSAGE_IN);
903 scsiWriteByte(0x02); // save data pointer
904 scsiWriteByte(0x04); // disconnect msg.
905
906 // For now, the caller is responsible for tracking the disconnected
907 // state, and calling scsiReconnect.
908 // Ideally the client would exit their loop and we'd implement this
909 // as part of scsiPoll
910 int phase = scsiDev.phase;
911 enter_BusFree();
912 scsiDev.phase = phase;
913 }
914
915 int scsiReconnect()
916 {
917 int reconnected = 0;
918
919 int sel = SCSI_ReadFilt(SCSI_Filt_SEL);
920 int bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
921 if (!sel && !bsy)
922 {
923 CyDelayUs(1);
924 sel = SCSI_ReadFilt(SCSI_Filt_SEL);
925 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
926 }
927
928 if (!sel && !bsy)
929 {
930 // Arbitrate.
931 ledOn();
932 uint8_t scsiIdMask = 1 << scsiDev.target->targetId;
933 SCSI_Out_Bits_Write(scsiIdMask);
934 SCSI_Out_Ctl_Write(1); // Write bits manually.
935 SCSI_SetPin(SCSI_Out_BSY);
936
937 CyDelayUs(3); // arbitrate delay. 2.4us.
938
939 uint8_t dbx = scsiReadDBxPins();
940 sel = SCSI_ReadFilt(SCSI_Filt_SEL);
941 if (sel || ((dbx ^ scsiIdMask) > scsiIdMask))
942 {
943 // Lost arbitration.
944 SCSI_Out_Ctl_Write(0);
945 SCSI_ClearPin(SCSI_Out_BSY);
946 ledOff();
947 }
948 else
949 {
950 // Won arbitration
951 SCSI_SetPin(SCSI_Out_SEL);
952 CyDelayUs(1); // Bus clear + Bus settle.
953
954 // Reselection phase
955 SCSI_CTL_PHASE_Write(__scsiphase_io);
956 SCSI_Out_Bits_Write(scsiIdMask | (1 << scsiDev.initiatorId));
957 scsiDeskewDelay(); // 2 deskew delays
958 scsiDeskewDelay(); // 2 deskew delays
959 SCSI_ClearPin(SCSI_Out_BSY);
960 CyDelayUs(1); // Bus Settle Delay
961
962 uint32_t waitStart_ms = getTime_ms();
963 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
964 // Wait for initiator.
965 while (
966 !bsy &&
967 !scsiDev.resetFlag &&
968 (elapsedTime_ms(waitStart_ms) < 250))
969 {
970 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
971 }
972
973 if (bsy)
974 {
975 SCSI_SetPin(SCSI_Out_BSY);
976 scsiDeskewDelay(); // 2 deskew delays
977 scsiDeskewDelay(); // 2 deskew delays
978 SCSI_ClearPin(SCSI_Out_SEL);
979
980 // Prepare for the initial IDENTIFY message.
981 SCSI_Out_Ctl_Write(0);
982 scsiEnterPhase(MESSAGE_IN);
983
984 // Send identify command
985 scsiWriteByte(0x80);
986
987 scsiEnterPhase(scsiDev.phase);
988 reconnected = 1;
989 }
990 else
991 {
992 // reselect timeout.
993 SCSI_Out_Ctl_Write(0);
994 SCSI_ClearPin(SCSI_Out_SEL);
995 SCSI_CTL_PHASE_Write(0);
996 ledOff();
997 }
998 }
999 }
1000 return reconnected;
1001 }
1002
1003 #pragma GCC pop_options