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