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"
37 // Global SCSI device state.
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);
50 static void doReserveRelease(void);
52 static void enter_BusFree()
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
)
61 if (scsiDev
.status
!= GOOD
&& isDebugEnabled())
63 // We want to capture debug information for failure cases.
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);
71 // Wait for the initiator to cease driving signals
72 // Bus settle delay + bus clear delay = 1200ns
76 scsiDev
.phase
= BUS_FREE
;
79 static void enter_MessageIn(uint8 message
)
81 scsiDev
.msgIn
= message
;
82 scsiDev
.phase
= MESSAGE_IN
;
85 void process_MessageIn()
87 scsiEnterPhase(MESSAGE_IN
);
88 scsiWriteByte(scsiDev
.msgIn
);
90 if (unlikely(scsiDev
.atnFlag
))
92 // If there was a parity error, we go
93 // back to MESSAGE_OUT first, get out parity error message, then come
96 else if ((scsiDev
.msgIn
== MSG_LINKED_COMMAND_COMPLETE
) ||
97 (scsiDev
.msgIn
== MSG_LINKED_COMMAND_COMPLETE_WITH_FLAG
))
99 // Go back to the command phase and start again.
100 scsiDev
.phase
= COMMAND
;
101 scsiDev
.parityError
= 0;
103 scsiDev
.savedDataPtr
= 0;
105 scsiDev
.status
= GOOD
;
107 transfer
.currentBlock
= 0;
109 else /*if (scsiDev.msgIn == MSG_COMMAND_COMPLETE)*/
115 static void messageReject()
117 scsiEnterPhase(MESSAGE_IN
);
118 scsiWriteByte(MSG_REJECT
);
121 static void enter_Status(uint8 status
)
123 scsiDev
.status
= status
;
124 scsiDev
.phase
= STATUS
;
126 scsiDev
.lastStatus
= scsiDev
.status
;
127 scsiDev
.lastSense
= scsiDev
.target
->sense
.code
;
128 scsiDev
.lastSenseASC
= scsiDev
.target
->sense
.asc
;
131 void process_Status()
133 scsiEnterPhase(STATUS
);
137 uint8 control
= scsiDev
.cdb
[scsiDev
.cdbLen
- 1];
138 if ((scsiDev
.status
== GOOD
) && (control
& 0x01))
141 scsiDev
.status
= INTERMEDIATE
;
144 message
= MSG_LINKED_COMMAND_COMPLETE_WITH_FLAG
;
148 message
= MSG_LINKED_COMMAND_COMPLETE
;
153 message
= MSG_COMMAND_COMPLETE
;
155 scsiWriteByte(scsiDev
.status
);
157 scsiDev
.lastStatus
= scsiDev
.status
;
158 scsiDev
.lastSense
= scsiDev
.target
->sense
.code
;
159 scsiDev
.lastSenseASC
= scsiDev
.target
->sense
.asc
;
162 // Command Complete occurs AFTER a valid status has been
163 // sent. then we go bus-free.
164 enter_MessageIn(message
);
167 static void enter_DataIn(int len
)
169 scsiDev
.dataLen
= len
;
170 scsiDev
.phase
= DATA_IN
;
173 static void process_DataIn()
177 if (scsiDev
.dataLen
> sizeof(scsiDev
.data
))
179 scsiDev
.dataLen
= sizeof(scsiDev
.data
);
182 len
= scsiDev
.dataLen
- scsiDev
.dataPtr
;
185 scsiEnterPhase(DATA_IN
);
186 scsiWrite(scsiDev
.data
+ scsiDev
.dataPtr
, len
);
187 scsiDev
.dataPtr
+= len
;
190 if ((scsiDev
.dataPtr
>= scsiDev
.dataLen
) &&
191 (transfer
.currentBlock
== transfer
.blocks
))
197 static void process_DataOut()
201 if (scsiDev
.dataLen
> sizeof(scsiDev
.data
))
203 scsiDev
.dataLen
= sizeof(scsiDev
.data
);
206 scsiDev
.parityError
= 0;
207 len
= scsiDev
.dataLen
- scsiDev
.dataPtr
;
210 scsiEnterPhase(DATA_OUT
);
212 scsiRead(scsiDev
.data
+ scsiDev
.dataPtr
, len
);
213 scsiDev
.dataPtr
+= len
;
215 if (scsiDev
.parityError
&&
216 (scsiDev
.target
->cfg
->flags
& CONFIG_ENABLE_PARITY
) &&
219 scsiDev
.target
->sense
.code
= ABORTED_COMMAND
;
220 scsiDev
.target
->sense
.asc
= SCSI_PARITY_ERROR
;
221 enter_Status(CHECK_CONDITION
);
225 if ((scsiDev
.dataPtr
>= scsiDev
.dataLen
) &&
226 (transfer
.currentBlock
== transfer
.blocks
))
228 if (scsiDev
.postDataOutHook
!= NULL
)
230 scsiDev
.postDataOutHook();
239 static const uint8 CmdGroupBytes
[8] = {6, 10, 10, 6, 6, 12, 6, 6};
240 static void process_Command()
246 scsiEnterPhase(COMMAND
);
247 scsiDev
.parityError
= 0;
249 memset(scsiDev
.cdb
, 0, sizeof(scsiDev
.cdb
));
250 scsiDev
.cdb
[0] = scsiReadByte();
252 group
= scsiDev
.cdb
[0] >> 5;
253 scsiDev
.cdbLen
= CmdGroupBytes
[group
];
254 scsiRead(scsiDev
.cdb
+ 1, scsiDev
.cdbLen
- 1);
256 command
= scsiDev
.cdb
[0];
258 // Prefer LUN's set by IDENTIFY messages for newer hosts.
261 scsiDev
.lun
= scsiDev
.cdb
[1] >> 5;
264 control
= scsiDev
.cdb
[scsiDev
.cdbLen
- 1];
268 if (unlikely(scsiDev
.resetFlag
))
270 // Don't log bogus commands
272 memset(scsiDev
.cdb
, 0xff, sizeof(scsiDev
.cdb
));
275 else if (scsiDev
.parityError
&&
276 (scsiDev
.target
->cfg
->flags
& CONFIG_ENABLE_PARITY
) &&
279 scsiDev
.target
->sense
.code
= ABORTED_COMMAND
;
280 scsiDev
.target
->sense
.asc
= SCSI_PARITY_ERROR
;
281 enter_Status(CHECK_CONDITION
);
283 else if ((control
& 0x02) && ((control
& 0x01) == 0))
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
);
290 else if (command
== 0x12)
294 else if (command
== 0x03)
297 uint32 allocLength
= scsiDev
.cdb
[4];
299 // As specified by the SASI and SCSI1 standard.
300 // Newer initiators won't be specifying 0 anyway.
301 if (allocLength
== 0) allocLength
= 4;
303 memset(scsiDev
.data
, 0, 256); // Max possible alloc length
304 scsiDev
.data
[0] = 0xF0;
305 scsiDev
.data
[2] = scsiDev
.target
->sense
.code
& 0x0F;
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
;
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
;
317 // Silently truncate results. SCSI-2 spec 8.2.14.
318 enter_DataIn(allocLength
);
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
;
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
))
331 scsiDev
.target
->sense
.code
= UNIT_ATTENTION
;
332 scsiDev
.target
->sense
.asc
= scsiDev
.target
->unitAttention
;
334 // If initiator doesn't do REQUEST SENSE for the next command, then
336 scsiDev
.target
->unitAttention
= 0;
338 enter_Status(CHECK_CONDITION
);
340 else if (scsiDev
.lun
)
342 scsiDev
.target
->sense
.code
= ILLEGAL_REQUEST
;
343 scsiDev
.target
->sense
.asc
= LOGICAL_UNIT_NOT_SUPPORTED
;
344 enter_Status(CHECK_CONDITION
);
346 else if (command
== 0x17 || command
== 0x16)
350 else if ((scsiDev
.target
->reservedId
>= 0) &&
351 (scsiDev
.target
->reservedId
!= scsiDev
.initiatorId
))
353 enter_Status(CONFLICT
);
355 else if (scsiDiskCommand())
358 // check for the performance-critical read/write
361 else if (command
== 0x1C)
363 scsiReceiveDiagnostic();
365 else if (command
== 0x1D)
367 scsiSendDiagnostic();
369 else if (command
== 0x3B)
373 else if (command
== 0x3C)
378 !scsiCDRomCommand() &&
381 scsiDev
.target
->sense
.code
= ILLEGAL_REQUEST
;
382 scsiDev
.target
->sense
.asc
= INVALID_COMMAND_OPERATION_CODE
;
383 enter_Status(CHECK_CONDITION
);
387 if (scsiDev
.phase
== COMMAND
) // No status set, and not in DATA_IN
394 static void doReserveRelease()
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];
402 (!thirdPty
&& (scsiDev
.initiatorId
== scsiDev
.target
->reservedId
)) ||
404 (scsiDev
.target
->reserverId
== scsiDev
.initiatorId
) &&
405 (scsiDev
.target
->reservedId
== thirdPtyId
)
408 if (extentReservation
)
411 scsiDev
.target
->sense
.code
= ILLEGAL_REQUEST
;
412 scsiDev
.target
->sense
.asc
= INVALID_FIELD_IN_CDB
;
413 enter_Status(CHECK_CONDITION
);
415 else if (command
== 0x17) // release
417 if ((scsiDev
.target
->reservedId
< 0) || canRelease
)
419 scsiDev
.target
->reservedId
= -1;
420 scsiDev
.target
->reserverId
= -1;
424 enter_Status(CONFLICT
);
427 else // assume reserve.
429 if ((scsiDev
.target
->reservedId
< 0) || canRelease
)
431 scsiDev
.target
->reserverId
= scsiDev
.initiatorId
;
434 scsiDev
.target
->reservedId
= thirdPtyId
;
438 scsiDev
.target
->reservedId
= scsiDev
.initiatorId
;
443 // Already reserved by someone else!
444 enter_Status(CONFLICT
);
449 static void scsiReset()
455 SCSI_Out_Ctl_Write(0);
457 scsiDev
.parityError
= 0;
458 scsiDev
.phase
= BUS_FREE
;
460 scsiDev
.resetFlag
= 0;
465 if (scsiDev
.target
->unitAttention
!= POWER_ON_RESET
)
467 scsiDev
.target
->unitAttention
= SCSI_BUS_RESET
;
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
;
474 scsiDev
.target
= NULL
;
477 scsiDev
.postDataOutHook
= NULL
;
479 // Sleep to allow the bus to settle down a bit.
480 // We must be ready again within the "Reset to selection time" of
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
490 static void enter_SelectionPhase()
492 // Ignore stale versions of this flag, but ensure we know the
493 // current value if the flag is still set.
495 scsiDev
.parityError
= 0;
497 scsiDev
.savedDataPtr
= 0;
499 scsiDev
.status
= GOOD
;
500 scsiDev
.phase
= SELECTION
;
502 scsiDev
.discPriv
= 0;
503 scsiDev
.compatMode
= 0;
505 scsiDev
.initiatorId
= -1;
506 scsiDev
.target
= NULL
;
509 transfer
.currentBlock
= 0;
511 scsiDev
.postDataOutHook
= NULL
;
514 static void process_SelectionPhase()
516 int sel
= SCSI_ReadFilt(SCSI_Filt_SEL
);
517 int bsy
= SCSI_ReadFilt(SCSI_Filt_BSY
);
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
);
527 TargetState
* target
= NULL
;
528 for (tgtIndex
= 0; tgtIndex
< MAX_SCSI_TARGETS
; ++tgtIndex
)
530 if (mask
& (1 << scsiDev
.targets
[tgtIndex
].targetId
))
532 target
= &scsiDev
.targets
[tgtIndex
];
538 (goodParity
|| !(target
->cfg
->flags
& CONFIG_ENABLE_PARITY
) || !atnFlag
) &&
539 likely(maskBitCount
<= 2))
541 scsiDev
.target
= target
;
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
;
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
)
554 target
->unitAttention
= 0;
555 scsiDev
.compatMode
= 1;
558 // We've been selected!
559 // Assert BSY - Selection success!
560 // must happen within 200us (Selection abort time) of seeing our
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
);
569 // Wait until the end of the selection phase.
570 while (likely(!scsiDev
.resetFlag
))
572 if (!SCSI_ReadFilt(SCSI_Filt_SEL
))
578 // Save our initiator now that we're no longer in a time-critical
580 // SCSI1/SASI initiators may not set their own ID.
583 uint8_t initiatorMask
= mask
^ (1 << target
->targetId
);
584 scsiDev
.initiatorId
= -1;
585 for (i
= 0; i
< 8; ++i
)
587 if (initiatorMask
& (1 << i
))
589 scsiDev
.initiatorId
= i
;
595 scsiDev
.phase
= COMMAND
;
599 scsiDev
.phase
= BUS_BUSY
;
603 static void process_MessageOut()
605 scsiEnterPhase(MESSAGE_OUT
);
608 scsiDev
.parityError
= 0;
609 scsiDev
.msgOut
= scsiReadByte();
612 if (scsiDev
.parityError
&&
613 (scsiDev
.target
->cfg
->flags
& CONFIG_ENABLE_PARITY
) &&
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
)
624 // Go-back and try the message again.
626 scsiDev
.parityError
= 0;
628 else if (scsiDev
.msgOut
== 0x00)
630 // COMMAND COMPLETE. but why would the target be receiving this ? nfi.
633 else if (scsiDev
.msgOut
== 0x06)
639 else if (scsiDev
.msgOut
== 0x0C)
645 scsiDev
.target
->unitAttention
= SCSI_BUS_RESET
;
647 // ANY initiator can reset the reservation state via this message.
648 scsiDev
.target
->reservedId
= -1;
649 scsiDev
.target
->reserverId
= -1;
652 else if (scsiDev
.msgOut
== 0x05)
654 // Initiate Detected Error
657 else if (scsiDev
.msgOut
== 0x0F)
662 else if (scsiDev
.msgOut
== 0x10)
668 else if (scsiDev
.msgOut
== MSG_REJECT
)
672 scsiDev
.resetFlag
= 1;
674 else if (scsiDev
.msgOut
== 0x08)
678 else if (scsiDev
.msgOut
== 0x09)
680 // Message Parity Error
681 // Go back and re-send the last message.
682 scsiDev
.phase
= MESSAGE_IN
;
684 else if (scsiDev
.msgOut
& 0x80) // 0x80 -> 0xFF
687 if ((scsiDev
.msgOut
& 0x18) || // Reserved bits set.
688 (scsiDev
.msgOut
& 0x20)) // We don't have any target routines!
693 scsiDev
.lun
= scsiDev
.msgOut
& 0x7;
695 ((scsiDev
.msgOut
& 0x40) && (scsiDev
.initiatorId
>= 0))
698 else if (scsiDev
.msgOut
>= 0x20 && scsiDev
.msgOut
<= 0x2F)
700 // Two byte message. We don't support these. read and discard.
703 else if (scsiDev
.msgOut
== 0x01)
708 int msgLen
= scsiReadByte();
709 if (msgLen
== 0) msgLen
= 256;
711 for (i
= 0; i
< msgLen
&& !scsiDev
.resetFlag
; ++i
)
714 extmsg
[i
] = scsiReadByte();
717 if (extmsg
[0] == 3 && msgLen
== 2) // Wide Data Request
719 // Negotiate down to 8bit
720 scsiEnterPhase(MESSAGE_IN
);
721 static const uint8_t WDTR
[] = {0x01, 0x02, 0x03, 0x00};
722 scsiWrite(WDTR
, sizeof(WDTR
));
724 else if (extmsg
[0] == 1 && msgLen
== 5) // Synchronous data request
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
));
742 // Re-check the ATN flag in case it stays asserted.
743 scsiDev
.atnFlag
|= SCSI_ReadFilt(SCSI_Filt_ATN
);
748 if (unlikely(scsiDev
.resetFlag
))
751 if ((scsiDev
.resetFlag
= SCSI_ReadFilt(SCSI_Filt_RST
)))
753 // Still in reset phase. Do not try and process any commands.
758 switch (scsiDev
.phase
)
761 if (SCSI_ReadFilt(SCSI_Filt_BSY
))
763 scsiDev
.phase
= BUS_BUSY
;
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
))
771 enter_SelectionPhase();
776 // Someone is using the bus. Perhaps they are trying to
778 if (SCSI_ReadFilt(SCSI_Filt_SEL
))
780 enter_SelectionPhase();
782 else if (!SCSI_ReadFilt(SCSI_Filt_BSY
))
784 scsiDev
.phase
= BUS_FREE
;
789 // TODO Support reselection.
793 process_SelectionPhase();
797 // Not currently supported!
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.
806 process_MessageOut();
815 scsiDev
.atnFlag
|= SCSI_ReadFilt(SCSI_Filt_ATN
);
818 process_MessageOut();
827 scsiDev
.atnFlag
|= SCSI_ReadFilt(SCSI_Filt_ATN
);
830 process_MessageOut();
839 scsiDev
.atnFlag
|= SCSI_ReadFilt(SCSI_Filt_ATN
);
842 process_MessageOut();
851 scsiDev
.atnFlag
|= SCSI_ReadFilt(SCSI_Filt_ATN
);
854 process_MessageOut();
864 process_MessageOut();
872 scsiDev
.resetFlag
= 1;
873 scsiDev
.phase
= BUS_FREE
;
874 scsiDev
.target
= NULL
;
877 for (i
= 0; i
< MAX_SCSI_TARGETS
; ++i
)
879 const TargetConfig
* cfg
= getConfigByIndex(i
);
880 if (cfg
&& (cfg
->scsiId
& CONFIG_TARGET_ENABLED
))
882 scsiDev
.targets
[i
].targetId
= cfg
->scsiId
& CONFIG_TARGET_ID_BITS
;
883 scsiDev
.targets
[i
].cfg
= cfg
;
885 scsiDev
.targets
[i
].liveCfg
.bytesPerSector
= cfg
->bytesPerSector
;
889 scsiDev
.targets
[i
].targetId
= 0xff;
890 scsiDev
.targets
[i
].cfg
= NULL
;
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
;
900 void scsiDisconnect()
902 scsiEnterPhase(MESSAGE_IN
);
903 scsiWriteByte(0x02); // save data pointer
904 scsiWriteByte(0x04); // disconnect msg.
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
;
912 scsiDev
.phase
= phase
;
919 int sel
= SCSI_ReadFilt(SCSI_Filt_SEL
);
920 int bsy
= SCSI_ReadFilt(SCSI_Filt_BSY
);
924 sel
= SCSI_ReadFilt(SCSI_Filt_SEL
);
925 bsy
= SCSI_ReadFilt(SCSI_Filt_BSY
);
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
);
937 CyDelayUs(3); // arbitrate delay. 2.4us.
939 uint8_t dbx
= scsiReadDBxPins();
940 sel
= SCSI_ReadFilt(SCSI_Filt_SEL
);
941 if (sel
|| ((dbx
^ scsiIdMask
) > scsiIdMask
))
944 SCSI_Out_Ctl_Write(0);
945 SCSI_ClearPin(SCSI_Out_BSY
);
951 SCSI_SetPin(SCSI_Out_SEL
);
952 CyDelayUs(1); // Bus clear + Bus settle.
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
962 uint32_t waitStart_ms
= getTime_ms();
963 bsy
= SCSI_ReadFilt(SCSI_Filt_BSY
);
964 // Wait for initiator.
967 !scsiDev
.resetFlag
&&
968 (elapsedTime_ms(waitStart_ms
) < 250))
970 bsy
= SCSI_ReadFilt(SCSI_Filt_BSY
);
975 SCSI_SetPin(SCSI_Out_BSY
);
976 scsiDeskewDelay(); // 2 deskew delays
977 scsiDeskewDelay(); // 2 deskew delays
978 SCSI_ClearPin(SCSI_Out_SEL
);
980 // Prepare for the initial IDENTIFY message.
981 SCSI_Out_Ctl_Write(0);
982 scsiEnterPhase(MESSAGE_IN
);
984 // Send identify command
987 scsiEnterPhase(scsiDev
.phase
);
993 SCSI_Out_Ctl_Write(0);
994 SCSI_ClearPin(SCSI_Out_SEL
);
995 SCSI_CTL_PHASE_Write(0);
1003 #pragma GCC pop_options