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/>.
21 #include "diagnostic.h"
35 // Global SCSI device state.
36 ScsiDevice scsiDev S2S_DMA_ALIGN
;
38 static void enter_SelectionPhase(void);
39 static void process_SelectionPhase(void);
40 static void enter_BusFree(void);
41 static void enter_MessageIn(uint8_t message
);
42 static void enter_Status(uint8_t status
);
43 static void enter_DataIn(int len
);
44 static void process_DataIn(void);
45 static void process_DataOut(void);
46 static void process_Command(void);
48 static void doReserveRelease(void);
50 static void enter_BusFree()
52 // This delay probably isn't needed for most SCSI hosts, but it won't
53 // hurt either. It's possible some of the samplers needed this delay.
54 if (scsiDev
.compatMode
< COMPAT_SCSI2
)
60 if (scsiDev
.status
!= GOOD
&& isDebugEnabled())
62 // We want to capture debug information for failure cases.
69 // Wait for the initiator to cease driving signals
70 // Bus settle delay + bus clear delay = 1200ns
75 scsiDev
.phase
= BUS_FREE
;
78 static void enter_MessageIn(uint8_t message
)
80 scsiDev
.msgIn
= message
;
81 scsiDev
.phase
= MESSAGE_IN
;
84 void process_MessageIn()
86 scsiEnterPhase(MESSAGE_IN
);
87 scsiWriteByte(scsiDev
.msgIn
);
89 if (unlikely(scsiDev
.atnFlag
))
91 // If there was a parity error, we go
92 // back to MESSAGE_OUT first, get out parity error message, then come
95 else if ((scsiDev
.msgIn
== MSG_LINKED_COMMAND_COMPLETE
) ||
96 (scsiDev
.msgIn
== MSG_LINKED_COMMAND_COMPLETE_WITH_FLAG
))
98 // Go back to the command phase and start again.
99 scsiDev
.phase
= COMMAND
;
100 scsiDev
.parityError
= 0;
102 scsiDev
.savedDataPtr
= 0;
104 scsiDev
.status
= GOOD
;
106 transfer
.currentBlock
= 0;
108 else /*if (scsiDev.msgIn == MSG_COMMAND_COMPLETE)*/
114 static void messageReject()
116 scsiEnterPhase(MESSAGE_IN
);
117 scsiWriteByte(MSG_REJECT
);
120 static void enter_Status(uint8_t status
)
122 scsiDev
.status
= status
;
123 scsiDev
.phase
= STATUS
;
125 scsiDev
.lastStatus
= scsiDev
.status
;
126 scsiDev
.lastSense
= scsiDev
.target
->sense
.code
;
127 scsiDev
.lastSenseASC
= scsiDev
.target
->sense
.asc
;
130 void process_Status()
132 scsiEnterPhase(STATUS
);
136 uint8_t control
= scsiDev
.cdb
[scsiDev
.cdbLen
- 1];
137 if ((scsiDev
.status
== GOOD
) && (control
& 0x01))
140 scsiDev
.status
= INTERMEDIATE
;
143 message
= MSG_LINKED_COMMAND_COMPLETE_WITH_FLAG
;
147 message
= MSG_LINKED_COMMAND_COMPLETE
;
152 message
= MSG_COMMAND_COMPLETE
;
154 scsiWriteByte(scsiDev
.status
);
156 scsiDev
.lastStatus
= scsiDev
.status
;
157 scsiDev
.lastSense
= scsiDev
.target
->sense
.code
;
158 scsiDev
.lastSenseASC
= scsiDev
.target
->sense
.asc
;
160 // Command Complete occurs AFTER a valid status has been
161 // sent. then we go bus-free.
162 enter_MessageIn(message
);
165 static void enter_DataIn(int len
)
167 scsiDev
.dataLen
= len
;
168 scsiDev
.phase
= DATA_IN
;
171 static void process_DataIn()
175 if (scsiDev
.dataLen
> sizeof(scsiDev
.data
))
177 scsiDev
.dataLen
= sizeof(scsiDev
.data
);
180 len
= scsiDev
.dataLen
- scsiDev
.dataPtr
;
183 scsiEnterPhase(DATA_IN
);
184 scsiWrite(scsiDev
.data
+ scsiDev
.dataPtr
, len
);
185 scsiDev
.dataPtr
+= len
;
188 if ((scsiDev
.dataPtr
>= scsiDev
.dataLen
) &&
189 (transfer
.currentBlock
== transfer
.blocks
))
195 static void process_DataOut()
199 if (scsiDev
.dataLen
> sizeof(scsiDev
.data
))
201 scsiDev
.dataLen
= sizeof(scsiDev
.data
);
204 scsiDev
.parityError
= 0;
205 len
= scsiDev
.dataLen
- scsiDev
.dataPtr
;
208 scsiEnterPhase(DATA_OUT
);
210 scsiRead(scsiDev
.data
+ scsiDev
.dataPtr
, len
);
211 scsiDev
.dataPtr
+= len
;
213 if (scsiDev
.parityError
&&
214 (scsiDev
.boardCfg
.flags
& S2S_CFG_ENABLE_PARITY
) &&
215 (scsiDev
.compatMode
>= COMPAT_SCSI2
))
217 scsiDev
.target
->sense
.code
= ABORTED_COMMAND
;
218 scsiDev
.target
->sense
.asc
= SCSI_PARITY_ERROR
;
219 enter_Status(CHECK_CONDITION
);
223 if ((scsiDev
.dataPtr
>= scsiDev
.dataLen
) &&
224 (transfer
.currentBlock
== transfer
.blocks
))
226 if (scsiDev
.postDataOutHook
!= NULL
)
228 scsiDev
.postDataOutHook();
237 static const uint8_t CmdGroupBytes
[8] = {6, 10, 10, 6, 6, 12, 6, 6};
238 static void process_Command()
244 scsiEnterPhase(COMMAND
);
245 scsiDev
.parityError
= 0;
247 memset(scsiDev
.cdb
+ 6, 0, sizeof(scsiDev
.cdb
) - 6);
248 scsiRead(scsiDev
.cdb
, 6);
250 group
= scsiDev
.cdb
[0] >> 5;
251 scsiDev
.cdbLen
= CmdGroupBytes
[group
];
252 if (scsiDev
.cdbLen
- 6 > 0)
254 scsiRead(scsiDev
.cdb
+ 6, scsiDev
.cdbLen
- 6);
257 command
= scsiDev
.cdb
[0];
259 // Prefer LUN's set by IDENTIFY messages for newer hosts.
262 scsiDev
.lun
= scsiDev
.cdb
[1] >> 5;
265 control
= scsiDev
.cdb
[scsiDev
.cdbLen
- 1];
268 const S2S_TargetCfg
* cfg
= scsiDev
.target
->cfg
;
270 if (unlikely(scsiDev
.resetFlag
))
272 // Don't log bogus commands
274 memset(scsiDev
.cdb
, 0xff, sizeof(scsiDev
.cdb
));
277 else if (scsiDev
.parityError
&&
278 (scsiDev
.boardCfg
.flags
& S2S_CFG_ENABLE_PARITY
) &&
279 (scsiDev
.compatMode
>= COMPAT_SCSI2
))
281 scsiDev
.target
->sense
.code
= ABORTED_COMMAND
;
282 scsiDev
.target
->sense
.asc
= SCSI_PARITY_ERROR
;
283 enter_Status(CHECK_CONDITION
);
285 else if ((control
& 0x02) && ((control
& 0x01) == 0))
287 // FLAG set without LINK flag.
288 scsiDev
.target
->sense
.code
= ILLEGAL_REQUEST
;
289 scsiDev
.target
->sense
.asc
= INVALID_FIELD_IN_CDB
;
290 enter_Status(CHECK_CONDITION
);
292 else if (command
== 0x12)
296 else if (command
== 0x03)
299 uint32_t allocLength
= scsiDev
.cdb
[4];
301 // As specified by the SASI and SCSI1 standard.
302 // Newer initiators won't be specifying 0 anyway.
303 if (allocLength
== 0) allocLength
= 4;
305 memset(scsiDev
.data
, 0, 256); // Max possible alloc length
306 scsiDev
.data
[0] = 0xF0;
307 scsiDev
.data
[2] = scsiDev
.target
->sense
.code
& 0x0F;
309 scsiDev
.data
[3] = transfer
.lba
>> 24;
310 scsiDev
.data
[4] = transfer
.lba
>> 16;
311 scsiDev
.data
[5] = transfer
.lba
>> 8;
312 scsiDev
.data
[6] = transfer
.lba
;
314 // Additional bytes if there are errors to report
315 scsiDev
.data
[7] = 10; // additional length
316 scsiDev
.data
[12] = scsiDev
.target
->sense
.asc
>> 8;
317 scsiDev
.data
[13] = scsiDev
.target
->sense
.asc
;
319 // Silently truncate results. SCSI-2 spec 8.2.14.
320 enter_DataIn(allocLength
);
322 // This is a good time to clear out old sense information.
323 scsiDev
.target
->sense
.code
= NO_SENSE
;
324 scsiDev
.target
->sense
.asc
= NO_ADDITIONAL_SENSE_INFORMATION
;
326 // Some old SCSI drivers do NOT properly support
327 // unitAttention. eg. the Mac Plus would trigger a SCSI reset
328 // on receiving the unit attention response on boot, thus
329 // triggering another unit attention condition.
330 else if (scsiDev
.target
->unitAttention
&&
331 (scsiDev
.boardCfg
.flags
& S2S_CFG_ENABLE_UNIT_ATTENTION
))
333 scsiDev
.target
->sense
.code
= UNIT_ATTENTION
;
334 scsiDev
.target
->sense
.asc
= scsiDev
.target
->unitAttention
;
336 // If initiator doesn't do REQUEST SENSE for the next command, then
338 scsiDev
.target
->unitAttention
= 0;
340 enter_Status(CHECK_CONDITION
);
342 else if (scsiDev
.lun
)
344 scsiDev
.target
->sense
.code
= ILLEGAL_REQUEST
;
345 scsiDev
.target
->sense
.asc
= LOGICAL_UNIT_NOT_SUPPORTED
;
346 enter_Status(CHECK_CONDITION
);
348 else if (command
== 0x17 || command
== 0x16)
352 else if ((scsiDev
.target
->reservedId
>= 0) &&
353 (scsiDev
.target
->reservedId
!= scsiDev
.initiatorId
))
355 enter_Status(CONFLICT
);
357 // Handle odd device types first that may override basic read and
358 // write commands. Will fall-through to generic disk handling.
359 else if (((cfg
->deviceType
== S2S_CFG_OPTICAL
) && scsiCDRomCommand()) ||
360 ((cfg
->deviceType
== S2S_CFG_SEQUENTIAL
) && scsiTapeCommand()) ||
361 ((cfg
->deviceType
== S2S_CFG_MO
) && scsiMOCommand()))
365 else if (scsiDiskCommand())
368 // check for the performance-critical read/write
371 else if (command
== 0x1C)
373 scsiReceiveDiagnostic();
375 else if (command
== 0x1D)
377 scsiSendDiagnostic();
379 else if (command
== 0x3B)
383 else if (command
== 0x3C)
387 else if (!scsiModeCommand())
389 scsiDev
.target
->sense
.code
= ILLEGAL_REQUEST
;
390 scsiDev
.target
->sense
.asc
= INVALID_COMMAND_OPERATION_CODE
;
391 enter_Status(CHECK_CONDITION
);
395 if (scsiDev
.phase
== COMMAND
) // No status set, and not in DATA_IN
402 static void doReserveRelease()
404 int extentReservation
= scsiDev
.cdb
[1] & 1;
405 int thirdPty
= scsiDev
.cdb
[1] & 0x10;
406 int thirdPtyId
= (scsiDev
.cdb
[1] >> 1) & 0x7;
407 uint8_t command
= scsiDev
.cdb
[0];
410 (!thirdPty
&& (scsiDev
.initiatorId
== scsiDev
.target
->reservedId
)) ||
412 (scsiDev
.target
->reserverId
== scsiDev
.initiatorId
) &&
413 (scsiDev
.target
->reservedId
== thirdPtyId
)
416 if (extentReservation
)
419 scsiDev
.target
->sense
.code
= ILLEGAL_REQUEST
;
420 scsiDev
.target
->sense
.asc
= INVALID_FIELD_IN_CDB
;
421 enter_Status(CHECK_CONDITION
);
423 else if (command
== 0x17) // release
425 if ((scsiDev
.target
->reservedId
< 0) || canRelease
)
427 scsiDev
.target
->reservedId
= -1;
428 scsiDev
.target
->reserverId
= -1;
432 enter_Status(CONFLICT
);
435 else // assume reserve.
437 if ((scsiDev
.target
->reservedId
< 0) || canRelease
)
439 scsiDev
.target
->reserverId
= scsiDev
.initiatorId
;
442 scsiDev
.target
->reservedId
= thirdPtyId
;
446 scsiDev
.target
->reservedId
= scsiDev
.initiatorId
;
451 // Already reserved by someone else!
452 enter_Status(CONFLICT
);
457 static void scsiReset()
464 scsiDev
.parityError
= 0;
465 scsiDev
.phase
= BUS_FREE
;
467 scsiDev
.resetFlag
= 0;
469 scsiDev
.compatMode
= COMPAT_UNKNOWN
;
473 if (scsiDev
.target
->unitAttention
!= POWER_ON_RESET
)
475 scsiDev
.target
->unitAttention
= SCSI_BUS_RESET
;
477 scsiDev
.target
->reservedId
= -1;
478 scsiDev
.target
->reserverId
= -1;
479 scsiDev
.target
->sense
.code
= NO_SENSE
;
480 scsiDev
.target
->sense
.asc
= NO_ADDITIONAL_SENSE_INFORMATION
;
482 scsiDev
.target
= NULL
;
485 scsiDev
.postDataOutHook
= NULL
;
487 // Sleep to allow the bus to settle down a bit.
488 // We must be ready again within the "Reset to selection time" of
490 // There is no guarantee that the RST line will be negated by then.
491 // NOTE: We could be connected and powered by USB for configuration,
492 // in which case TERMPWR cannot be supplied, and reset will ALWAYS
493 // be true. Therefore, the sleep here must be slow to avoid slowing
495 s2s_delay_ms(1); // 1ms.
498 static void enter_SelectionPhase()
500 // Ignore stale versions of this flag, but ensure we know the
501 // current value if the flag is still set.
503 scsiDev
.parityError
= 0;
505 scsiDev
.savedDataPtr
= 0;
507 scsiDev
.status
= GOOD
;
508 scsiDev
.phase
= SELECTION
;
510 scsiDev
.discPriv
= 0;
512 scsiDev
.initiatorId
= -1;
513 scsiDev
.target
= NULL
;
516 transfer
.currentBlock
= 0;
518 scsiDev
.postDataOutHook
= NULL
;
521 static void process_SelectionPhase()
524 // Many SCSI1 samplers that use a 5380 chip need a delay of at least 1ms.
525 // The Mac Plus boot-time (ie. rom code) selection abort time
526 // is < 1ms and must have no delay (standard suggests 250ms abort time)
527 // Most newer SCSI2 hosts don't care either way.
528 if (scsiDev
.boardCfg
.selectionDelay
== 255) // auto
530 if (scsiDev
.compatMode
< COMPAT_SCSI2
)
535 else if (scsiDev
.boardCfg
.selectionDelay
!= 0)
537 s2s_delay_ms(scsiDev
.boardCfg
.selectionDelay
);
540 uint8_t selStatus
= *SCSI_STS_SELECTED
;
543 TargetState
* target
= NULL
;
544 for (tgtIndex
= 0; tgtIndex
< S2S_MAX_TARGETS
; ++tgtIndex
)
546 if (scsiDev
.targets
[tgtIndex
].targetId
== (selStatus
& 7))
548 target
= &scsiDev
.targets
[tgtIndex
];
552 if ((target
!= NULL
) && (selStatus
& 0x40))
553 // TODO (goodParity || !(scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY) || !atnFlag)
555 // We've been selected!
556 // Assert BSY - Selection success!
557 // must happen within 200us (Selection abort time) of seeing our
559 // (Note: the initiator will be waiting the "Selection time-out delay"
560 // for our BSY response, which is actually a very generous 250ms)
564 scsiDev
.target
= target
;
566 // Do we enter MESSAGE OUT immediately ? SCSI 1 and 2 standards says
567 // move to MESSAGE OUT if ATN is true before we assert BSY.
568 // The initiator should assert ATN with SEL.
569 scsiDev
.atnFlag
= selStatus
& 0x80;
572 // Unit attention breaks many older SCSI hosts. Disable it completely
573 // for SCSI-1 (and older) hosts, regardless of our configured setting.
574 // Enable the compatability mode also as many SASI and SCSI1
575 // controllers don't generate parity bits.
576 if (!scsiDev
.atnFlag
)
578 target
->unitAttention
= 0;
579 scsiDev
.compatMode
= COMPAT_SCSI1
;
581 else if (!(scsiDev
.boardCfg
.flags
& S2S_CFG_ENABLE_SCSI2
))
583 scsiDev
.compatMode
= COMPAT_SCSI1
;
585 else if (scsiDev
.compatMode
== COMPAT_UNKNOWN
)
587 scsiDev
.compatMode
= COMPAT_SCSI2
;
593 // Save our initiator now that we're no longer in a time-critical
595 // SCSI1/SASI initiators may not set their own ID.
596 scsiDev
.initiatorId
= (selStatus
>> 3) & 0x7;
598 while (likely(!scsiDev
.resetFlag
) && scsiStatusSEL())
600 // Wait until the end of the selection phase.
603 scsiDev
.phase
= COMMAND
;
607 scsiDev
.phase
= BUS_BUSY
;
611 static void process_MessageOut()
613 scsiEnterPhase(MESSAGE_OUT
);
616 scsiDev
.parityError
= 0;
617 scsiDev
.msgOut
= scsiReadByte();
620 if (scsiDev
.parityError
&&
621 (scsiDev
.boardCfg
.flags
& S2S_CFG_ENABLE_PARITY
) &&
622 (scsiDev
.compatMode
>= COMPAT_SCSI2
))
624 // Skip the remaining message bytes, and then start the MESSAGE_OUT
625 // phase again from the start. The initiator will re-send the
626 // same set of messages.
627 while (scsiStatusATN() && !scsiDev
.resetFlag
)
632 // Go-back and try the message again.
634 scsiDev
.parityError
= 0;
636 else if (scsiDev
.msgOut
== 0x00)
638 // COMMAND COMPLETE. but why would the target be receiving this ? nfi.
641 else if (scsiDev
.msgOut
== 0x06)
647 else if (scsiDev
.msgOut
== 0x0C)
653 scsiDev
.target
->unitAttention
= SCSI_BUS_RESET
;
655 // ANY initiator can reset the reservation state via this message.
656 scsiDev
.target
->reservedId
= -1;
657 scsiDev
.target
->reserverId
= -1;
660 else if (scsiDev
.msgOut
== 0x05)
662 // Initiate Detected Error
665 else if (scsiDev
.msgOut
== 0x0F)
670 else if (scsiDev
.msgOut
== 0x10)
676 else if (scsiDev
.msgOut
== MSG_REJECT
)
680 scsiDev
.resetFlag
= 1;
682 else if (scsiDev
.msgOut
== 0x08)
686 else if (scsiDev
.msgOut
== 0x09)
688 // Message Parity Error
689 // Go back and re-send the last message.
690 scsiDev
.phase
= MESSAGE_IN
;
692 else if (scsiDev
.msgOut
& 0x80) // 0x80 -> 0xFF
695 if ((scsiDev
.msgOut
& 0x18) || // Reserved bits set.
696 (scsiDev
.msgOut
& 0x20)) // We don't have any target routines!
701 scsiDev
.lun
= scsiDev
.msgOut
& 0x7;
703 ((scsiDev
.msgOut
& 0x40) && (scsiDev
.initiatorId
>= 0))
706 else if (scsiDev
.msgOut
>= 0x20 && scsiDev
.msgOut
<= 0x2F)
708 // Two byte message. We don't support these. read and discard.
711 if (scsiDev
.msgOut
== 0x23) {
712 // Ignore Wide Residue. We're only 8 bit anyway.
717 else if (scsiDev
.msgOut
== 0x01)
722 int msgLen
= scsiReadByte();
723 if (msgLen
== 0) msgLen
= 256;
725 for (i
= 0; i
< msgLen
&& !scsiDev
.resetFlag
; ++i
)
728 extmsg
[i
] = scsiReadByte();
731 if (extmsg
[0] == 3 && msgLen
== 2) // Wide Data Request
733 // Negotiate down to 8bit
734 scsiEnterPhase(MESSAGE_IN
);
735 static const uint8_t WDTR
[] = {0x01, 0x02, 0x03, 0x00};
736 scsiWrite(WDTR
, sizeof(WDTR
));
738 else if (extmsg
[0] == 1 && msgLen
== 3) // Synchronous data request
740 // Negotiate back to async
741 scsiEnterPhase(MESSAGE_IN
);
742 static const uint8_t SDTR
[] = {0x01, 0x03, 0x01, 0x00, 0x00};
743 scsiWrite(SDTR
, sizeof(SDTR
));
756 // Re-check the ATN flag in case it stays asserted.
757 scsiDev
.atnFlag
|= scsiStatusATN();
762 if (unlikely(scsiDev
.resetFlag
))
765 if ((scsiDev
.resetFlag
= scsiStatusRST()))
767 // Still in reset phase. Do not try and process any commands.
772 switch (scsiDev
.phase
)
777 scsiDev
.phase
= BUS_BUSY
;
779 // The Arbitration phase is optional for SCSI1/SASI hosts if there is only
780 // one initiator in the chain. Support this by moving
781 // straight to selection if SEL is asserted.
782 // ie. the initiator won't assert BSY and it's own ID before moving to selection.
783 else if (*SCSI_STS_SELECTED
)
785 enter_SelectionPhase();
790 // Someone is using the bus. Perhaps they are trying to
792 if (*SCSI_STS_SELECTED
)
794 enter_SelectionPhase();
796 else if (!scsiStatusBSY())
798 scsiDev
.phase
= BUS_FREE
;
803 // TODO Support reselection.
807 process_SelectionPhase();
811 // Not currently supported!
815 // Do not check ATN here. SCSI 1 & 2 initiators must set ATN
816 // and SEL together upon entering the selection phase if they
817 // want to send a message (IDENTIFY) immediately.
820 process_MessageOut();
829 scsiDev
.atnFlag
|= scsiStatusATN();
832 process_MessageOut();
841 scsiDev
.atnFlag
|= scsiStatusATN();
844 process_MessageOut();
853 scsiDev
.atnFlag
|= scsiStatusATN();
856 process_MessageOut();
865 scsiDev
.atnFlag
|= scsiStatusATN();
868 process_MessageOut();
878 process_MessageOut();
885 static int firstInit
= 1;
888 scsiDev
.resetFlag
= 1;
889 scsiDev
.phase
= BUS_FREE
;
890 scsiDev
.target
= NULL
;
891 scsiDev
.compatMode
= COMPAT_UNKNOWN
;
894 for (i
= 0; i
< S2S_MAX_TARGETS
; ++i
)
896 const S2S_TargetCfg
* cfg
= s2s_getConfigByIndex(i
);
897 if (cfg
&& (cfg
->scsiId
& S2S_CFG_TARGET_ENABLED
))
899 scsiDev
.targets
[i
].targetId
= cfg
->scsiId
& S2S_CFG_TARGET_ID_BITS
;
900 scsiDev
.targets
[i
].cfg
= cfg
;
902 scsiDev
.targets
[i
].liveCfg
.bytesPerSector
= cfg
->bytesPerSector
;
906 scsiDev
.targets
[i
].targetId
= 0xff;
907 scsiDev
.targets
[i
].cfg
= NULL
;
909 scsiDev
.targets
[i
].reservedId
= -1;
910 scsiDev
.targets
[i
].reserverId
= -1;
913 scsiDev
.targets
[i
].unitAttention
= POWER_ON_RESET
;
917 scsiDev
.targets
[i
].unitAttention
= PARAMETERS_CHANGED
;
919 scsiDev
.targets
[i
].sense
.code
= NO_SENSE
;
920 scsiDev
.targets
[i
].sense
.asc
= NO_ADDITIONAL_SENSE_INFORMATION
;
926 void scsiDisconnect()
928 scsiEnterPhase(MESSAGE_IN);
929 scsiWriteByte(0x02); // save data pointer
930 scsiWriteByte(0x04); // disconnect msg.
932 // For now, the caller is responsible for tracking the disconnected
933 // state, and calling scsiReconnect.
934 // Ideally the client would exit their loop and we'd implement this
935 // as part of scsiPoll
936 int phase = scsiDev.phase;
938 scsiDev.phase = phase;
947 int sel = SCSI_ReadFilt(SCSI_Filt_SEL);
948 int bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
952 sel = SCSI_ReadFilt(SCSI_Filt_SEL);
953 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
960 uint8_t scsiIdMask = 1 << scsiDev.target->targetId;
961 SCSI_Out_Bits_Write(scsiIdMask);
962 SCSI_Out_Ctl_Write(1); // Write bits manually.
963 SCSI_SetPin(SCSI_Out_BSY);
965 s2s_delay_us(3); // arbitrate delay. 2.4us.
967 uint8_t dbx = scsiReadDBxPins();
968 sel = SCSI_ReadFilt(SCSI_Filt_SEL);
969 if (sel || ((dbx ^ scsiIdMask) > scsiIdMask))
972 SCSI_Out_Ctl_Write(0);
973 SCSI_ClearPin(SCSI_Out_BSY);
979 SCSI_SetPin(SCSI_Out_SEL);
980 s2s_delay_us(1); // Bus clear + Bus settle.
983 SCSI_CTL_PHASE_Write(__scsiphase_io);
984 SCSI_Out_Bits_Write(scsiIdMask | (1 << scsiDev.initiatorId));
985 scsiDeskewDelay(); // 2 deskew delays
986 scsiDeskewDelay(); // 2 deskew delays
987 SCSI_ClearPin(SCSI_Out_BSY);
988 s2s_delay_us(1); // Bus Settle Delay
990 uint32_t waitStart_ms = getTime_ms();
991 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
992 // Wait for initiator.
995 !scsiDev.resetFlag &&
996 (elapsedTime_ms(waitStart_ms) < 250))
998 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
1003 SCSI_SetPin(SCSI_Out_BSY);
1004 scsiDeskewDelay(); // 2 deskew delays
1005 scsiDeskewDelay(); // 2 deskew delays
1006 SCSI_ClearPin(SCSI_Out_SEL);
1008 // Prepare for the initial IDENTIFY message.
1009 SCSI_Out_Ctl_Write(0);
1010 scsiEnterPhase(MESSAGE_IN);
1012 // Send identify command
1013 scsiWriteByte(0x80);
1015 scsiEnterPhase(scsiDev.phase);
1020 // reselect timeout.
1021 SCSI_Out_Ctl_Write(0);
1022 SCSI_ClearPin(SCSI_Out_SEL);
1023 SCSI_CTL_PHASE_Write(0);