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
;
484 for (int i
= 0; i
< S2S_MAX_TARGETS
; ++i
)
486 scsiDev
.target
[i
].syncOffset
= 0;
487 scsiDev
.target
[i
].syncPeriod
= 0;
492 scsiDev
.postDataOutHook
= NULL
;
494 // Sleep to allow the bus to settle down a bit.
495 // We must be ready again within the "Reset to selection time" of
497 // There is no guarantee that the RST line will be negated by then.
498 // NOTE: We could be connected and powered by USB for configuration,
499 // in which case TERMPWR cannot be supplied, and reset will ALWAYS
500 // be true. Therefore, the sleep here must be slow to avoid slowing
502 s2s_delay_ms(1); // 1ms.
505 static void enter_SelectionPhase()
507 // Ignore stale versions of this flag, but ensure we know the
508 // current value if the flag is still set.
510 scsiDev
.parityError
= 0;
512 scsiDev
.savedDataPtr
= 0;
514 scsiDev
.status
= GOOD
;
515 scsiDev
.phase
= SELECTION
;
517 scsiDev
.discPriv
= 0;
519 scsiDev
.initiatorId
= -1;
520 scsiDev
.target
= NULL
;
523 transfer
.currentBlock
= 0;
525 scsiDev
.postDataOutHook
= NULL
;
527 scsiDev
.needSyncNegotiationAck
= 0;
530 static void process_SelectionPhase()
533 // Many SCSI1 samplers that use a 5380 chip need a delay of at least 1ms.
534 // The Mac Plus boot-time (ie. rom code) selection abort time
535 // is < 1ms and must have no delay (standard suggests 250ms abort time)
536 // Most newer SCSI2 hosts don't care either way.
537 if (scsiDev
.boardCfg
.selectionDelay
== 255) // auto
539 if (scsiDev
.compatMode
< COMPAT_SCSI2
)
544 else if (scsiDev
.boardCfg
.selectionDelay
!= 0)
546 s2s_delay_ms(scsiDev
.boardCfg
.selectionDelay
);
549 uint8_t selStatus
= *SCSI_STS_SELECTED
;
552 TargetState
* target
= NULL
;
553 for (tgtIndex
= 0; tgtIndex
< S2S_MAX_TARGETS
; ++tgtIndex
)
555 if (scsiDev
.targets
[tgtIndex
].targetId
== (selStatus
& 7))
557 target
= &scsiDev
.targets
[tgtIndex
];
561 if ((target
!= NULL
) && (selStatus
& 0x40))
562 // TODO (goodParity || !(scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY) || !atnFlag)
564 // We've been selected!
565 // Assert BSY - Selection success!
566 // must happen within 200us (Selection abort time) of seeing our
568 // (Note: the initiator will be waiting the "Selection time-out delay"
569 // for our BSY response, which is actually a very generous 250ms)
573 scsiDev
.target
= target
;
575 // Do we enter MESSAGE OUT immediately ? SCSI 1 and 2 standards says
576 // move to MESSAGE OUT if ATN is true before we assert BSY.
577 // The initiator should assert ATN with SEL.
578 scsiDev
.atnFlag
= selStatus
& 0x80;
581 // Unit attention breaks many older SCSI hosts. Disable it completely
582 // for SCSI-1 (and older) hosts, regardless of our configured setting.
583 // Enable the compatability mode also as many SASI and SCSI1
584 // controllers don't generate parity bits.
585 if (!scsiDev
.atnFlag
)
587 target
->unitAttention
= 0;
588 scsiDev
.compatMode
= COMPAT_SCSI1
;
590 else if (!(scsiDev
.boardCfg
.flags
& S2S_CFG_ENABLE_SCSI2
))
592 scsiDev
.compatMode
= COMPAT_SCSI1
;
594 else if (scsiDev
.compatMode
== COMPAT_UNKNOWN
)
596 scsiDev
.compatMode
= COMPAT_SCSI2
;
602 // Save our initiator now that we're no longer in a time-critical
604 // SCSI1/SASI initiators may not set their own ID.
605 scsiDev
.initiatorId
= (selStatus
>> 3) & 0x7;
607 while (likely(!scsiDev
.resetFlag
) && scsiStatusSEL())
609 // Wait until the end of the selection phase.
612 scsiDev
.phase
= COMMAND
;
616 scsiDev
.phase
= BUS_BUSY
;
620 static void process_MessageOut()
622 scsiEnterPhase(MESSAGE_OUT
);
625 scsiDev
.parityError
= 0;
626 scsiDev
.msgOut
= scsiReadByte();
629 if (scsiDev
.parityError
&&
630 (scsiDev
.boardCfg
.flags
& S2S_CFG_ENABLE_PARITY
) &&
631 (scsiDev
.compatMode
>= COMPAT_SCSI2
))
633 // Skip the remaining message bytes, and then start the MESSAGE_OUT
634 // phase again from the start. The initiator will re-send the
635 // same set of messages.
636 while (scsiStatusATN() && !scsiDev
.resetFlag
)
641 // Go-back and try the message again.
643 scsiDev
.parityError
= 0;
645 else if (scsiDev
.msgOut
== 0x00)
647 // COMMAND COMPLETE. but why would the target be receiving this ? nfi.
650 else if (scsiDev
.msgOut
== 0x06)
656 else if (scsiDev
.msgOut
== 0x0C)
662 scsiDev
.target
->unitAttention
= SCSI_BUS_RESET
;
664 // ANY initiator can reset the reservation state via this message.
665 scsiDev
.target
->reservedId
= -1;
666 scsiDev
.target
->reserverId
= -1;
668 // Cancel any sync negotiation
669 scsiDev
.target
->syncOffset
= 0;
670 scsiDev
.target
->syncPeriod
= 0;
674 else if (scsiDev
.msgOut
== 0x05)
676 // Initiate Detected Error
679 else if (scsiDev
.msgOut
== 0x0F)
684 else if (scsiDev
.msgOut
== 0x10)
690 else if (scsiDev
.msgOut
== MSG_REJECT
)
695 if (scsiDev
.needSyncNegotiationAck
)
697 scsiDev
.target
->syncOffset
= 0;
698 scsiDev
.target
->syncPeriod
= 0;
699 scsiDev
.needSyncNegotiationAck
= 0;
702 else if (scsiDev
.msgOut
== 0x08)
706 else if (scsiDev
.msgOut
== 0x09)
708 // Message Parity Error
709 // Go back and re-send the last message.
710 scsiDev
.phase
= MESSAGE_IN
;
712 else if (scsiDev
.msgOut
& 0x80) // 0x80 -> 0xFF
715 if ((scsiDev
.msgOut
& 0x18) || // Reserved bits set.
716 (scsiDev
.msgOut
& 0x20)) // We don't have any target routines!
721 scsiDev
.lun
= scsiDev
.msgOut
& 0x7;
723 ((scsiDev
.msgOut
& 0x40) && (scsiDev
.initiatorId
>= 0))
726 else if (scsiDev
.msgOut
>= 0x20 && scsiDev
.msgOut
<= 0x2F)
728 // Two byte message. We don't support these. read and discard.
731 if (scsiDev
.msgOut
== 0x23) {
732 // Ignore Wide Residue. We're only 8 bit anyway.
737 else if (scsiDev
.msgOut
== 0x01)
742 int msgLen
= scsiReadByte();
743 if (msgLen
== 0) msgLen
= 256;
745 for (i
= 0; i
< msgLen
&& !scsiDev
.resetFlag
; ++i
)
748 extmsg
[i
] = scsiReadByte();
751 if (extmsg
[0] == 3 && msgLen
== 2) // Wide Data Request
753 // Negotiate down to 8bit
754 scsiEnterPhase(MESSAGE_IN
);
755 static const uint8_t WDTR
[] = {0x01, 0x02, 0x03, 0x00};
756 scsiWrite(WDTR
, sizeof(WDTR
));
758 else if (extmsg
[0] == 1 && msgLen
== 3) // Synchronous data request
760 int transferPeriod
= extmsg
[3];
761 int offset
= extmsg
[4];
763 if (transferPeriod
> 50) // 200ns, 5MB/s
765 scsiDev
.target
->syncOffset
= 0;
766 scsiDev
.target
->syncPeriod
= 0;
768 scsiDev
.target
->syncOffset
= offset
< 15 ? offset
: 15;
769 if (transferPeriod
<= 25)
771 scsiDev
.target
->syncPeriod
= 25; // 10MB/s
773 scsiDev
.target
->syncPeriod
= 50; // 5MB/s
777 scsiEnterPhase(MESSAGE_IN
);
778 uint8_t SDTR
[] = {0x01, 0x03, 0x01, scsiDev
.target
->syncPeriod
, scsiDev
.target
->syncOffset
};
779 scsiWrite(SDTR
, sizeof(SDTR
));
780 scsiDev
.needSyncNegotiationAck
= 1; // Check if this message is rejected.
793 // Re-check the ATN flag in case it stays asserted.
794 scsiDev
.atnFlag
|= scsiStatusATN();
796 if (!scsiDev
.atnFlag
)
798 // Message wasn't rejected!
799 scsiDev
.needSyncNegotiationAck
= 0;
805 if (unlikely(scsiDev
.resetFlag
))
808 if ((scsiDev
.resetFlag
= scsiStatusRST()))
810 // Still in reset phase. Do not try and process any commands.
815 switch (scsiDev
.phase
)
820 scsiDev
.phase
= BUS_BUSY
;
822 // The Arbitration phase is optional for SCSI1/SASI hosts if there is only
823 // one initiator in the chain. Support this by moving
824 // straight to selection if SEL is asserted.
825 // ie. the initiator won't assert BSY and it's own ID before moving to selection.
826 else if (*SCSI_STS_SELECTED
)
828 enter_SelectionPhase();
833 // Someone is using the bus. Perhaps they are trying to
835 if (*SCSI_STS_SELECTED
)
837 enter_SelectionPhase();
839 else if (!scsiStatusBSY())
841 scsiDev
.phase
= BUS_FREE
;
846 // TODO Support reselection.
850 process_SelectionPhase();
854 // Not currently supported!
858 // Do not check ATN here. SCSI 1 & 2 initiators must set ATN
859 // and SEL together upon entering the selection phase if they
860 // want to send a message (IDENTIFY) immediately.
863 process_MessageOut();
872 scsiDev
.atnFlag
|= scsiStatusATN();
875 process_MessageOut();
884 scsiDev
.atnFlag
|= scsiStatusATN();
887 process_MessageOut();
896 scsiDev
.atnFlag
|= scsiStatusATN();
899 process_MessageOut();
908 scsiDev
.atnFlag
|= scsiStatusATN();
911 process_MessageOut();
921 process_MessageOut();
928 static int firstInit
= 1;
931 scsiDev
.resetFlag
= 1;
932 scsiDev
.phase
= BUS_FREE
;
933 scsiDev
.target
= NULL
;
934 scsiDev
.compatMode
= COMPAT_UNKNOWN
;
937 for (i
= 0; i
< S2S_MAX_TARGETS
; ++i
)
939 const S2S_TargetCfg
* cfg
= s2s_getConfigByIndex(i
);
940 if (cfg
&& (cfg
->scsiId
& S2S_CFG_TARGET_ENABLED
))
942 scsiDev
.targets
[i
].targetId
= cfg
->scsiId
& S2S_CFG_TARGET_ID_BITS
;
943 scsiDev
.targets
[i
].cfg
= cfg
;
945 scsiDev
.targets
[i
].liveCfg
.bytesPerSector
= cfg
->bytesPerSector
;
949 scsiDev
.targets
[i
].targetId
= 0xff;
950 scsiDev
.targets
[i
].cfg
= NULL
;
952 scsiDev
.targets
[i
].reservedId
= -1;
953 scsiDev
.targets
[i
].reserverId
= -1;
956 scsiDev
.targets
[i
].unitAttention
= POWER_ON_RESET
;
960 scsiDev
.targets
[i
].unitAttention
= PARAMETERS_CHANGED
;
962 scsiDev
.targets
[i
].sense
.code
= NO_SENSE
;
963 scsiDev
.targets
[i
].sense
.asc
= NO_ADDITIONAL_SENSE_INFORMATION
;
965 scsiDev
.targets
[i
].syncOffset
= 0;
966 scsiDev
.targets
[i
].syncPeriod
= 0;
972 void scsiDisconnect()
974 scsiEnterPhase(MESSAGE_IN);
975 scsiWriteByte(0x02); // save data pointer
976 scsiWriteByte(0x04); // disconnect msg.
978 // For now, the caller is responsible for tracking the disconnected
979 // state, and calling scsiReconnect.
980 // Ideally the client would exit their loop and we'd implement this
981 // as part of scsiPoll
982 int phase = scsiDev.phase;
984 scsiDev.phase = phase;
993 int sel = SCSI_ReadFilt(SCSI_Filt_SEL);
994 int bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
998 sel = SCSI_ReadFilt(SCSI_Filt_SEL);
999 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
1006 uint8_t scsiIdMask = 1 << scsiDev.target->targetId;
1007 SCSI_Out_Bits_Write(scsiIdMask);
1008 SCSI_Out_Ctl_Write(1); // Write bits manually.
1009 SCSI_SetPin(SCSI_Out_BSY);
1011 s2s_delay_us(3); // arbitrate delay. 2.4us.
1013 uint8_t dbx = scsiReadDBxPins();
1014 sel = SCSI_ReadFilt(SCSI_Filt_SEL);
1015 if (sel || ((dbx ^ scsiIdMask) > scsiIdMask))
1017 // Lost arbitration.
1018 SCSI_Out_Ctl_Write(0);
1019 SCSI_ClearPin(SCSI_Out_BSY);
1025 SCSI_SetPin(SCSI_Out_SEL);
1026 s2s_delay_us(1); // Bus clear + Bus settle.
1028 // Reselection phase
1029 SCSI_CTL_PHASE_Write(__scsiphase_io);
1030 SCSI_Out_Bits_Write(scsiIdMask | (1 << scsiDev.initiatorId));
1031 scsiDeskewDelay(); // 2 deskew delays
1032 scsiDeskewDelay(); // 2 deskew delays
1033 SCSI_ClearPin(SCSI_Out_BSY);
1034 s2s_delay_us(1); // Bus Settle Delay
1036 uint32_t waitStart_ms = getTime_ms();
1037 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
1038 // Wait for initiator.
1041 !scsiDev.resetFlag &&
1042 (elapsedTime_ms(waitStart_ms) < 250))
1044 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
1049 SCSI_SetPin(SCSI_Out_BSY);
1050 scsiDeskewDelay(); // 2 deskew delays
1051 scsiDeskewDelay(); // 2 deskew delays
1052 SCSI_ClearPin(SCSI_Out_SEL);
1054 // Prepare for the initial IDENTIFY message.
1055 SCSI_Out_Ctl_Write(0);
1056 scsiEnterPhase(MESSAGE_IN);
1058 // Send identify command
1059 scsiWriteByte(0x80);
1061 scsiEnterPhase(scsiDev.phase);
1066 // reselect timeout.
1067 SCSI_Out_Ctl_Write(0);
1068 SCSI_ClearPin(SCSI_Out_SEL);
1069 SCSI_CTL_PHASE_Write(0);