e43afd2497fe9e2ace67cfd2c771bea9b0c3efff
[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[3];
761 int offset = extmsg[4];
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 <= 25)
770 {
771 scsiDev.target->syncPeriod = 25; // 10MB/s
772 } else {
773 scsiDev.target->syncPeriod = 50; // 5MB/s
774 }
775 }
776
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.
781 }
782 else
783 {
784 // Not supported
785 messageReject();
786 }
787 }
788 else
789 {
790 messageReject();
791 }
792
793 // Re-check the ATN flag in case it stays asserted.
794 scsiDev.atnFlag |= scsiStatusATN();
795
796 if (!scsiDev.atnFlag)
797 {
798 // Message wasn't rejected!
799 scsiDev.needSyncNegotiationAck = 0;
800 }
801 }
802
803 void scsiPoll(void)
804 {
805 if (unlikely(scsiDev.resetFlag))
806 {
807 scsiReset();
808 if ((scsiDev.resetFlag = scsiStatusRST()))
809 {
810 // Still in reset phase. Do not try and process any commands.
811 return;
812 }
813 }
814
815 switch (scsiDev.phase)
816 {
817 case BUS_FREE:
818 if (scsiStatusBSY())
819 {
820 scsiDev.phase = BUS_BUSY;
821 }
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)
827 {
828 enter_SelectionPhase();
829 }
830 break;
831
832 case BUS_BUSY:
833 // Someone is using the bus. Perhaps they are trying to
834 // select us.
835 if (*SCSI_STS_SELECTED)
836 {
837 enter_SelectionPhase();
838 }
839 else if (!scsiStatusBSY())
840 {
841 scsiDev.phase = BUS_FREE;
842 }
843 break;
844
845 case ARBITRATION:
846 // TODO Support reselection.
847 break;
848
849 case SELECTION:
850 process_SelectionPhase();
851 break;
852
853 case RESELECTION:
854 // Not currently supported!
855 break;
856
857 case COMMAND:
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.
861 if (scsiDev.atnFlag)
862 {
863 process_MessageOut();
864 }
865 else
866 {
867 process_Command();
868 }
869 break;
870
871 case DATA_IN:
872 scsiDev.atnFlag |= scsiStatusATN();
873 if (scsiDev.atnFlag)
874 {
875 process_MessageOut();
876 }
877 else
878 {
879 process_DataIn();
880 }
881 break;
882
883 case DATA_OUT:
884 scsiDev.atnFlag |= scsiStatusATN();
885 if (scsiDev.atnFlag)
886 {
887 process_MessageOut();
888 }
889 else
890 {
891 process_DataOut();
892 }
893 break;
894
895 case STATUS:
896 scsiDev.atnFlag |= scsiStatusATN();
897 if (scsiDev.atnFlag)
898 {
899 process_MessageOut();
900 }
901 else
902 {
903 process_Status();
904 }
905 break;
906
907 case MESSAGE_IN:
908 scsiDev.atnFlag |= scsiStatusATN();
909 if (scsiDev.atnFlag)
910 {
911 process_MessageOut();
912 }
913 else
914 {
915 process_MessageIn();
916 }
917
918 break;
919
920 case MESSAGE_OUT:
921 process_MessageOut();
922 break;
923 }
924 }
925
926 void scsiInit()
927 {
928 static int firstInit = 1;
929
930 scsiDev.atnFlag = 0;
931 scsiDev.resetFlag = 1;
932 scsiDev.phase = BUS_FREE;
933 scsiDev.target = NULL;
934 scsiDev.compatMode = COMPAT_UNKNOWN;
935
936 int i;
937 for (i = 0; i < S2S_MAX_TARGETS; ++i)
938 {
939 const S2S_TargetCfg* cfg = s2s_getConfigByIndex(i);
940 if (cfg && (cfg->scsiId & S2S_CFG_TARGET_ENABLED))
941 {
942 scsiDev.targets[i].targetId = cfg->scsiId & S2S_CFG_TARGET_ID_BITS;
943 scsiDev.targets[i].cfg = cfg;
944
945 scsiDev.targets[i].liveCfg.bytesPerSector = cfg->bytesPerSector;
946 }
947 else
948 {
949 scsiDev.targets[i].targetId = 0xff;
950 scsiDev.targets[i].cfg = NULL;
951 }
952 scsiDev.targets[i].reservedId = -1;
953 scsiDev.targets[i].reserverId = -1;
954 if (firstInit)
955 {
956 scsiDev.targets[i].unitAttention = POWER_ON_RESET;
957 }
958 else
959 {
960 scsiDev.targets[i].unitAttention = PARAMETERS_CHANGED;
961 }
962 scsiDev.targets[i].sense.code = NO_SENSE;
963 scsiDev.targets[i].sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;
964
965 scsiDev.targets[i].syncOffset = 0;
966 scsiDev.targets[i].syncPeriod = 0;
967 }
968 firstInit = 0;
969 }
970
971 /* TODO REENABLE
972 void scsiDisconnect()
973 {
974 scsiEnterPhase(MESSAGE_IN);
975 scsiWriteByte(0x02); // save data pointer
976 scsiWriteByte(0x04); // disconnect msg.
977
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;
983 enter_BusFree();
984 scsiDev.phase = phase;
985 }
986 */
987
988 /* TODO REENABLE
989 int scsiReconnect()
990 {
991 int reconnected = 0;
992
993 int sel = SCSI_ReadFilt(SCSI_Filt_SEL);
994 int bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
995 if (!sel && !bsy)
996 {
997 s2s_delay_us(1);
998 sel = SCSI_ReadFilt(SCSI_Filt_SEL);
999 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
1000 }
1001
1002 if (!sel && !bsy)
1003 {
1004 // Arbitrate.
1005 s2s_ledOn();
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);
1010
1011 s2s_delay_us(3); // arbitrate delay. 2.4us.
1012
1013 uint8_t dbx = scsiReadDBxPins();
1014 sel = SCSI_ReadFilt(SCSI_Filt_SEL);
1015 if (sel || ((dbx ^ scsiIdMask) > scsiIdMask))
1016 {
1017 // Lost arbitration.
1018 SCSI_Out_Ctl_Write(0);
1019 SCSI_ClearPin(SCSI_Out_BSY);
1020 s2s_ledOff();
1021 }
1022 else
1023 {
1024 // Won arbitration
1025 SCSI_SetPin(SCSI_Out_SEL);
1026 s2s_delay_us(1); // Bus clear + Bus settle.
1027
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
1035
1036 uint32_t waitStart_ms = getTime_ms();
1037 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
1038 // Wait for initiator.
1039 while (
1040 !bsy &&
1041 !scsiDev.resetFlag &&
1042 (elapsedTime_ms(waitStart_ms) < 250))
1043 {
1044 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
1045 }
1046
1047 if (bsy)
1048 {
1049 SCSI_SetPin(SCSI_Out_BSY);
1050 scsiDeskewDelay(); // 2 deskew delays
1051 scsiDeskewDelay(); // 2 deskew delays
1052 SCSI_ClearPin(SCSI_Out_SEL);
1053
1054 // Prepare for the initial IDENTIFY message.
1055 SCSI_Out_Ctl_Write(0);
1056 scsiEnterPhase(MESSAGE_IN);
1057
1058 // Send identify command
1059 scsiWriteByte(0x80);
1060
1061 scsiEnterPhase(scsiDev.phase);
1062 reconnected = 1;
1063 }
1064 else
1065 {
1066 // reselect timeout.
1067 SCSI_Out_Ctl_Write(0);
1068 SCSI_ClearPin(SCSI_Out_SEL);
1069 SCSI_CTL_PHASE_Write(0);
1070 s2s_ledOff();
1071 }
1072 }
1073 }
1074 return reconnected;
1075 }
1076 */
1077