1 // Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
3 // This file is part of SCSI2SD.
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.
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.
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")
25 #include "diagnostic.h"
39 // Global SCSI device state.
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);
52 static void doReserveRelease(void);
54 static void enter_BusFree()
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
)
63 if (scsiDev
.status
!= GOOD
&& isDebugEnabled())
65 // We want to capture debug information for failure cases.
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);
73 // Wait for the initiator to cease driving signals
74 // Bus settle delay + bus clear delay = 1200ns
78 scsiDev
.phase
= BUS_FREE
;
81 static void enter_MessageIn(uint8 message
)
83 scsiDev
.msgIn
= message
;
84 scsiDev
.phase
= MESSAGE_IN
;
87 void process_MessageIn()
89 scsiEnterPhase(MESSAGE_IN
);
90 scsiWriteByte(scsiDev
.msgIn
);
92 if (unlikely(scsiDev
.atnFlag
))
94 // If there was a parity error, we go
95 // back to MESSAGE_OUT first, get out parity error message, then come
98 else if ((scsiDev
.msgIn
== MSG_LINKED_COMMAND_COMPLETE
) ||
99 (scsiDev
.msgIn
== MSG_LINKED_COMMAND_COMPLETE_WITH_FLAG
))
101 // Go back to the command phase and start again.
102 scsiDev
.phase
= COMMAND
;
103 scsiDev
.parityError
= 0;
105 scsiDev
.savedDataPtr
= 0;
107 scsiDev
.status
= GOOD
;
109 transfer
.currentBlock
= 0;
111 else /*if (scsiDev.msgIn == MSG_COMMAND_COMPLETE)*/
117 static void messageReject()
119 scsiEnterPhase(MESSAGE_IN
);
120 scsiWriteByte(MSG_REJECT
);
123 static void enter_Status(uint8 status
)
125 scsiDev
.status
= status
;
126 scsiDev
.phase
= STATUS
;
128 scsiDev
.lastStatus
= scsiDev
.status
;
129 scsiDev
.lastSense
= scsiDev
.target
->sense
.code
;
130 scsiDev
.lastSenseASC
= scsiDev
.target
->sense
.asc
;
133 void process_Status()
135 scsiEnterPhase(STATUS
);
139 uint8 control
= scsiDev
.cdb
[scsiDev
.cdbLen
- 1];
140 if ((scsiDev
.status
== GOOD
) && (control
& 0x01))
143 scsiDev
.status
= INTERMEDIATE
;
146 message
= MSG_LINKED_COMMAND_COMPLETE_WITH_FLAG
;
150 message
= MSG_LINKED_COMMAND_COMPLETE
;
155 message
= MSG_COMMAND_COMPLETE
;
157 scsiWriteByte(scsiDev
.status
);
159 scsiDev
.lastStatus
= scsiDev
.status
;
160 scsiDev
.lastSense
= scsiDev
.target
->sense
.code
;
161 scsiDev
.lastSenseASC
= scsiDev
.target
->sense
.asc
;
164 // Command Complete occurs AFTER a valid status has been
165 // sent. then we go bus-free.
166 enter_MessageIn(message
);
169 static void enter_DataIn(int len
)
171 scsiDev
.dataLen
= len
;
172 scsiDev
.phase
= DATA_IN
;
175 static void process_DataIn()
179 if (scsiDev
.dataLen
> sizeof(scsiDev
.data
))
181 scsiDev
.dataLen
= sizeof(scsiDev
.data
);
184 len
= scsiDev
.dataLen
- scsiDev
.dataPtr
;
187 scsiEnterPhase(DATA_IN
);
188 scsiWrite(scsiDev
.data
+ scsiDev
.dataPtr
, len
);
189 scsiDev
.dataPtr
+= len
;
192 if ((scsiDev
.dataPtr
>= scsiDev
.dataLen
) &&
193 (transfer
.currentBlock
== transfer
.blocks
))
199 static void process_DataOut()
203 if (scsiDev
.dataLen
> sizeof(scsiDev
.data
))
205 scsiDev
.dataLen
= sizeof(scsiDev
.data
);
208 scsiDev
.parityError
= 0;
209 len
= scsiDev
.dataLen
- scsiDev
.dataPtr
;
212 scsiEnterPhase(DATA_OUT
);
214 scsiRead(scsiDev
.data
+ scsiDev
.dataPtr
, len
);
215 scsiDev
.dataPtr
+= len
;
217 if (scsiDev
.parityError
&&
218 (scsiDev
.boardCfg
.flags
& CONFIG_ENABLE_PARITY
) &&
219 (scsiDev
.compatMode
>= COMPAT_SCSI2
))
221 scsiDev
.target
->sense
.code
= ABORTED_COMMAND
;
222 scsiDev
.target
->sense
.asc
= SCSI_PARITY_ERROR
;
223 enter_Status(CHECK_CONDITION
);
227 if ((scsiDev
.dataPtr
>= scsiDev
.dataLen
) &&
228 (transfer
.currentBlock
== transfer
.blocks
))
230 if (scsiDev
.postDataOutHook
!= NULL
)
232 scsiDev
.postDataOutHook();
241 static const uint8 CmdGroupBytes
[8] = {6, 10, 10, 6, 6, 12, 6, 6};
242 static void process_Command()
248 scsiEnterPhase(COMMAND
);
249 scsiDev
.parityError
= 0;
251 memset(scsiDev
.cdb
, 0, sizeof(scsiDev
.cdb
));
252 scsiDev
.cdb
[0] = scsiReadByte();
254 group
= scsiDev
.cdb
[0] >> 5;
255 scsiDev
.cdbLen
= CmdGroupBytes
[group
];
256 scsiRead(scsiDev
.cdb
+ 1, scsiDev
.cdbLen
- 1);
258 command
= scsiDev
.cdb
[0];
260 // Prefer LUN's set by IDENTIFY messages for newer hosts.
263 scsiDev
.lun
= scsiDev
.cdb
[1] >> 5;
266 control
= scsiDev
.cdb
[scsiDev
.cdbLen
- 1];
269 TargetConfig
* cfg
= scsiDev
.target
->cfg
;
271 if (unlikely(scsiDev
.resetFlag
))
273 // Don't log bogus commands
275 memset(scsiDev
.cdb
, 0xff, sizeof(scsiDev
.cdb
));
278 else if (scsiDev
.parityError
&&
279 (scsiDev
.boardCfg
.flags
& CONFIG_ENABLE_PARITY
) &&
280 (scsiDev
.compatMode
>= COMPAT_SCSI2
))
282 scsiDev
.target
->sense
.code
= ABORTED_COMMAND
;
283 scsiDev
.target
->sense
.asc
= SCSI_PARITY_ERROR
;
284 enter_Status(CHECK_CONDITION
);
286 else if ((control
& 0x02) && ((control
& 0x01) == 0))
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
);
293 else if (command
== 0x12)
297 else if (command
== 0x03)
300 uint32 allocLength
= scsiDev
.cdb
[4];
302 // As specified by the SASI and SCSI1 standard.
303 // Newer initiators won't be specifying 0 anyway.
304 if (allocLength
== 0) allocLength
= 4;
306 memset(scsiDev
.data
, 0, 256); // Max possible alloc length
307 scsiDev
.data
[0] = 0xF0;
308 scsiDev
.data
[2] = scsiDev
.target
->sense
.code
& 0x0F;
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
;
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
;
320 // Silently truncate results. SCSI-2 spec 8.2.14.
321 enter_DataIn(allocLength
);
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
;
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
))
334 scsiDev
.target
->sense
.code
= UNIT_ATTENTION
;
335 scsiDev
.target
->sense
.asc
= scsiDev
.target
->unitAttention
;
337 // If initiator doesn't do REQUEST SENSE for the next command, then
339 scsiDev
.target
->unitAttention
= 0;
341 enter_Status(CHECK_CONDITION
);
343 else if (scsiDev
.lun
)
345 scsiDev
.target
->sense
.code
= ILLEGAL_REQUEST
;
346 scsiDev
.target
->sense
.asc
= LOGICAL_UNIT_NOT_SUPPORTED
;
347 enter_Status(CHECK_CONDITION
);
349 else if (command
== 0x17 || command
== 0x16)
353 else if ((scsiDev
.target
->reservedId
>= 0) &&
354 (scsiDev
.target
->reservedId
!= scsiDev
.initiatorId
))
356 enter_Status(CONFLICT
);
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()))
366 else if (scsiDiskCommand())
369 // check for the performance-critical read/write
372 else if (command
== 0x1C)
374 scsiReceiveDiagnostic();
376 else if (command
== 0x1D)
378 scsiSendDiagnostic();
380 else if (command
== 0x3B)
384 else if (command
== 0x3C)
388 else if (!scsiModeCommand())
390 scsiDev
.target
->sense
.code
= ILLEGAL_REQUEST
;
391 scsiDev
.target
->sense
.asc
= INVALID_COMMAND_OPERATION_CODE
;
392 enter_Status(CHECK_CONDITION
);
396 if (scsiDev
.phase
== COMMAND
) // No status set, and not in DATA_IN
403 static void doReserveRelease()
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];
411 (!thirdPty
&& (scsiDev
.initiatorId
== scsiDev
.target
->reservedId
)) ||
413 (scsiDev
.target
->reserverId
== scsiDev
.initiatorId
) &&
414 (scsiDev
.target
->reservedId
== thirdPtyId
)
417 if (extentReservation
)
420 scsiDev
.target
->sense
.code
= ILLEGAL_REQUEST
;
421 scsiDev
.target
->sense
.asc
= INVALID_FIELD_IN_CDB
;
422 enter_Status(CHECK_CONDITION
);
424 else if (command
== 0x17) // release
426 if ((scsiDev
.target
->reservedId
< 0) || canRelease
)
428 scsiDev
.target
->reservedId
= -1;
429 scsiDev
.target
->reserverId
= -1;
433 enter_Status(CONFLICT
);
436 else // assume reserve.
438 if ((scsiDev
.target
->reservedId
< 0) || canRelease
)
440 scsiDev
.target
->reserverId
= scsiDev
.initiatorId
;
443 scsiDev
.target
->reservedId
= thirdPtyId
;
447 scsiDev
.target
->reservedId
= scsiDev
.initiatorId
;
452 // Already reserved by someone else!
453 enter_Status(CONFLICT
);
458 static void scsiReset()
464 SCSI_Out_Ctl_Write(0);
466 scsiDev
.parityError
= 0;
467 scsiDev
.phase
= BUS_FREE
;
469 scsiDev
.resetFlag
= 0;
471 scsiDev
.compatMode
= COMPAT_UNKNOWN
;
475 if (scsiDev
.target
->unitAttention
!= POWER_ON_RESET
)
477 scsiDev
.target
->unitAttention
= SCSI_BUS_RESET
;
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
;
484 scsiDev
.target
= NULL
;
487 scsiDev
.postDataOutHook
= NULL
;
489 // Sleep to allow the bus to settle down a bit.
490 // We must be ready again within the "Reset to selection time" of
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
500 static void enter_SelectionPhase()
502 // Ignore stale versions of this flag, but ensure we know the
503 // current value if the flag is still set.
505 scsiDev
.parityError
= 0;
507 scsiDev
.savedDataPtr
= 0;
509 scsiDev
.status
= GOOD
;
510 scsiDev
.phase
= SELECTION
;
512 scsiDev
.discPriv
= 0;
514 scsiDev
.initiatorId
= -1;
515 scsiDev
.target
= NULL
;
518 transfer
.currentBlock
= 0;
520 scsiDev
.postDataOutHook
= NULL
;
523 static void process_SelectionPhase()
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
532 if (scsiDev
.compatMode
< COMPAT_SCSI2
)
537 else if (scsiDev
.boardCfg
.selectionDelay
!= 0)
539 CyDelay(scsiDev
.boardCfg
.selectionDelay
);
542 int sel
= SCSI_ReadFilt(SCSI_Filt_SEL
);
543 int bsy
= SCSI_ReadFilt(SCSI_Filt_BSY
);
544 int io
= SCSI_ReadPin(SCSI_In_IO
);
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
);
554 TargetState
* target
= NULL
;
555 for (tgtIndex
= 0; tgtIndex
< MAX_SCSI_TARGETS
; ++tgtIndex
)
557 if (mask
& (1 << scsiDev
.targets
[tgtIndex
].targetId
))
559 target
= &scsiDev
.targets
[tgtIndex
];
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
&&
568 (goodParity
|| !(scsiDev
.boardCfg
.flags
& CONFIG_ENABLE_PARITY
) || !atnFlag
) &&
569 likely(maskBitCount
<= 2))
571 // We've been selected!
572 // Assert BSY - Selection success!
573 // must happen within 200us (Selection abort time) of seeing our
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
);
580 scsiDev
.target
= target
;
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
;
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
)
594 target
->unitAttention
= 0;
595 scsiDev
.compatMode
= COMPAT_SCSI1
;
597 else if (!(scsiDev
.boardCfg
.flags
& CONFIG_ENABLE_SCSI2
))
599 scsiDev
.compatMode
= COMPAT_SCSI1
;
601 else if (scsiDev
.compatMode
== COMPAT_UNKNOWN
)
603 scsiDev
.compatMode
= COMPAT_SCSI2
;
609 // Save our initiator now that we're no longer in a time-critical
611 // SCSI1/SASI initiators may not set their own ID.
614 uint8_t initiatorMask
= mask
^ (1 << target
->targetId
);
615 scsiDev
.initiatorId
= -1;
616 for (i
= 0; i
< 8; ++i
)
618 if (initiatorMask
& (1 << i
))
620 scsiDev
.initiatorId
= i
;
626 // Wait until the end of the selection phase.
627 while (likely(!scsiDev
.resetFlag
))
629 if (!SCSI_ReadFilt(SCSI_Filt_SEL
))
635 scsiDev
.phase
= COMMAND
;
639 scsiDev
.phase
= BUS_BUSY
;
643 static void process_MessageOut()
645 scsiEnterPhase(MESSAGE_OUT
);
648 scsiDev
.parityError
= 0;
649 scsiDev
.msgOut
= scsiReadByte();
652 if (scsiDev
.parityError
&&
653 (scsiDev
.boardCfg
.flags
& CONFIG_ENABLE_PARITY
) &&
654 (scsiDev
.compatMode
>= COMPAT_SCSI2
))
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
)
664 // Go-back and try the message again.
666 scsiDev
.parityError
= 0;
668 else if (scsiDev
.msgOut
== 0x00)
670 // COMMAND COMPLETE. but why would the target be receiving this ? nfi.
673 else if (scsiDev
.msgOut
== 0x06)
679 else if (scsiDev
.msgOut
== 0x0C)
685 scsiDev
.target
->unitAttention
= SCSI_BUS_RESET
;
687 // ANY initiator can reset the reservation state via this message.
688 scsiDev
.target
->reservedId
= -1;
689 scsiDev
.target
->reserverId
= -1;
692 else if (scsiDev
.msgOut
== 0x05)
694 // Initiate Detected Error
697 else if (scsiDev
.msgOut
== 0x0F)
702 else if (scsiDev
.msgOut
== 0x10)
708 else if (scsiDev
.msgOut
== MSG_REJECT
)
712 scsiDev
.resetFlag
= 1;
714 else if (scsiDev
.msgOut
== 0x08)
718 else if (scsiDev
.msgOut
== 0x09)
720 // Message Parity Error
721 // Go back and re-send the last message.
722 scsiDev
.phase
= MESSAGE_IN
;
724 else if (scsiDev
.msgOut
& 0x80) // 0x80 -> 0xFF
727 if ((scsiDev
.msgOut
& 0x18) || // Reserved bits set.
728 (scsiDev
.msgOut
& 0x20)) // We don't have any target routines!
733 scsiDev
.lun
= scsiDev
.msgOut
& 0x7;
735 ((scsiDev
.msgOut
& 0x40) && (scsiDev
.initiatorId
>= 0))
738 else if (scsiDev
.msgOut
>= 0x20 && scsiDev
.msgOut
<= 0x2F)
740 // Two byte message. We don't support these. read and discard.
743 if (scsiDev
.msgOut
== 0x23) {
744 // Ignore Wide Residue. We're only 8 bit anyway.
749 else if (scsiDev
.msgOut
== 0x01)
754 int msgLen
= scsiReadByte();
755 if (msgLen
== 0) msgLen
= 256;
757 for (i
= 0; i
< msgLen
&& !scsiDev
.resetFlag
; ++i
)
760 extmsg
[i
] = scsiReadByte();
763 if (extmsg
[0] == 3 && msgLen
== 2) // Wide Data Request
765 // Negotiate down to 8bit
766 scsiEnterPhase(MESSAGE_IN
);
767 static const uint8_t WDTR
[] = {0x01, 0x02, 0x03, 0x00};
768 scsiWrite(WDTR
, sizeof(WDTR
));
770 else if (extmsg
[0] == 1 && msgLen
== 3) // Synchronous data request
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
));
788 // Re-check the ATN flag in case it stays asserted.
789 scsiDev
.atnFlag
|= SCSI_ReadFilt(SCSI_Filt_ATN
);
794 if (unlikely(scsiDev
.resetFlag
))
797 if ((scsiDev
.resetFlag
= SCSI_ReadFilt(SCSI_Filt_RST
)))
799 // Still in reset phase. Do not try and process any commands.
804 switch (scsiDev
.phase
)
807 if (SCSI_ReadFilt(SCSI_Filt_BSY
))
809 scsiDev
.phase
= BUS_BUSY
;
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
))
817 enter_SelectionPhase();
822 // Someone is using the bus. Perhaps they are trying to
824 if (SCSI_ReadFilt(SCSI_Filt_SEL
))
826 enter_SelectionPhase();
828 else if (!SCSI_ReadFilt(SCSI_Filt_BSY
))
830 scsiDev
.phase
= BUS_FREE
;
835 // TODO Support reselection.
839 process_SelectionPhase();
843 // Not currently supported!
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.
852 process_MessageOut();
861 scsiDev
.atnFlag
|= SCSI_ReadFilt(SCSI_Filt_ATN
);
864 process_MessageOut();
873 scsiDev
.atnFlag
|= SCSI_ReadFilt(SCSI_Filt_ATN
);
876 process_MessageOut();
885 scsiDev
.atnFlag
|= SCSI_ReadFilt(SCSI_Filt_ATN
);
888 process_MessageOut();
897 scsiDev
.atnFlag
|= SCSI_ReadFilt(SCSI_Filt_ATN
);
900 process_MessageOut();
910 process_MessageOut();
918 scsiDev
.resetFlag
= 1;
919 scsiDev
.phase
= BUS_FREE
;
920 scsiDev
.target
= NULL
;
921 scsiDev
.compatMode
= COMPAT_UNKNOWN
;
924 for (i
= 0; i
< MAX_SCSI_TARGETS
; ++i
)
926 const TargetConfig
* cfg
= getConfigByIndex(i
);
927 if (cfg
&& (cfg
->scsiId
& CONFIG_TARGET_ENABLED
))
929 scsiDev
.targets
[i
].targetId
= cfg
->scsiId
& CONFIG_TARGET_ID_BITS
;
930 scsiDev
.targets
[i
].cfg
= cfg
;
932 scsiDev
.targets
[i
].liveCfg
.bytesPerSector
= cfg
->bytesPerSector
;
936 scsiDev
.targets
[i
].targetId
= 0xff;
937 scsiDev
.targets
[i
].cfg
= NULL
;
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
;
947 void scsiDisconnect()
949 scsiEnterPhase(MESSAGE_IN
);
950 scsiWriteByte(0x02); // save data pointer
951 scsiWriteByte(0x04); // disconnect msg.
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
;
959 scsiDev
.phase
= phase
;
966 int sel
= SCSI_ReadFilt(SCSI_Filt_SEL
);
967 int bsy
= SCSI_ReadFilt(SCSI_Filt_BSY
);
971 sel
= SCSI_ReadFilt(SCSI_Filt_SEL
);
972 bsy
= SCSI_ReadFilt(SCSI_Filt_BSY
);
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
);
984 CyDelayUs(3); // arbitrate delay. 2.4us.
986 uint8_t dbx
= scsiReadDBxPins();
987 sel
= SCSI_ReadFilt(SCSI_Filt_SEL
);
988 if (sel
|| ((dbx
^ scsiIdMask
) > scsiIdMask
))
991 SCSI_Out_Ctl_Write(0);
992 SCSI_ClearPin(SCSI_Out_BSY
);
998 SCSI_SetPin(SCSI_Out_SEL
);
999 CyDelayUs(1); // Bus clear + Bus settle.
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
1009 uint32_t waitStart_ms
= getTime_ms();
1010 bsy
= SCSI_ReadFilt(SCSI_Filt_BSY
);
1011 // Wait for initiator.
1014 !scsiDev
.resetFlag
&&
1015 (elapsedTime_ms(waitStart_ms
) < 250))
1017 bsy
= SCSI_ReadFilt(SCSI_Filt_BSY
);
1022 SCSI_SetPin(SCSI_Out_BSY
);
1023 scsiDeskewDelay(); // 2 deskew delays
1024 scsiDeskewDelay(); // 2 deskew delays
1025 SCSI_ClearPin(SCSI_Out_SEL
);
1027 // Prepare for the initial IDENTIFY message.
1028 SCSI_Out_Ctl_Write(0);
1029 scsiEnterPhase(MESSAGE_IN
);
1031 // Send identify command
1032 scsiWriteByte(0x80);
1034 scsiEnterPhase(scsiDev
.phase
);
1039 // reselect timeout.
1040 SCSI_Out_Ctl_Write(0);
1041 SCSI_ClearPin(SCSI_Out_SEL
);
1042 SCSI_CTL_PHASE_Write(0);
1050 #pragma GCC pop_options