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