bbb3e4843a5572f0e522f936d9f79348e7b8afe0
[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 scsiDiskReset();
484
485 scsiDev.postDataOutHook = NULL;
486
487 // Sleep to allow the bus to settle down a bit.
488 // We must be ready again within the "Reset to selection time" of
489 // 250ms.
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
494 // USB comms
495 s2s_delay_ms(1); // 1ms.
496 }
497
498 static void enter_SelectionPhase()
499 {
500 // Ignore stale versions of this flag, but ensure we know the
501 // current value if the flag is still set.
502 scsiDev.atnFlag = 0;
503 scsiDev.parityError = 0;
504 scsiDev.dataPtr = 0;
505 scsiDev.savedDataPtr = 0;
506 scsiDev.dataLen = 0;
507 scsiDev.status = GOOD;
508 scsiDev.phase = SELECTION;
509 scsiDev.lun = -1;
510 scsiDev.discPriv = 0;
511
512 scsiDev.initiatorId = -1;
513 scsiDev.target = NULL;
514
515 transfer.blocks = 0;
516 transfer.currentBlock = 0;
517
518 scsiDev.postDataOutHook = NULL;
519 }
520
521 static void process_SelectionPhase()
522 {
523 // Selection delays.
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
529 {
530 if (scsiDev.compatMode < COMPAT_SCSI2)
531 {
532 s2s_delay_ms(1);
533 }
534 }
535 else if (scsiDev.boardCfg.selectionDelay != 0)
536 {
537 s2s_delay_ms(scsiDev.boardCfg.selectionDelay);
538 }
539
540 uint8_t selStatus = *SCSI_STS_SELECTED;
541
542 int tgtIndex;
543 TargetState* target = NULL;
544 for (tgtIndex = 0; tgtIndex < S2S_MAX_TARGETS; ++tgtIndex)
545 {
546 if (scsiDev.targets[tgtIndex].targetId == (selStatus & 7))
547 {
548 target = &scsiDev.targets[tgtIndex];
549 break;
550 }
551 }
552 if ((target != NULL) && (selStatus & 0x40))
553 // TODO (goodParity || !(scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY) || !atnFlag)
554 {
555 // We've been selected!
556 // Assert BSY - Selection success!
557 // must happen within 200us (Selection abort time) of seeing our
558 // ID + SEL.
559 // (Note: the initiator will be waiting the "Selection time-out delay"
560 // for our BSY response, which is actually a very generous 250ms)
561 *SCSI_CTRL_BSY = 1;
562 s2s_ledOn();
563
564 scsiDev.target = target;
565
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;
570
571
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)
577 {
578 target->unitAttention = 0;
579 scsiDev.compatMode = COMPAT_SCSI1;
580 }
581 else if (!(scsiDev.boardCfg.flags & S2S_CFG_ENABLE_SCSI2))
582 {
583 scsiDev.compatMode = COMPAT_SCSI1;
584 }
585 else if (scsiDev.compatMode == COMPAT_UNKNOWN)
586 {
587 scsiDev.compatMode = COMPAT_SCSI2;
588 }
589
590 scsiDev.selCount++;
591
592
593 // Save our initiator now that we're no longer in a time-critical
594 // section.
595 // SCSI1/SASI initiators may not set their own ID.
596 scsiDev.initiatorId = (selStatus >> 3) & 0x7;
597
598 while (likely(!scsiDev.resetFlag) && scsiStatusSEL())
599 {
600 // Wait until the end of the selection phase.
601 }
602
603 scsiDev.phase = COMMAND;
604 }
605 else if (!selStatus)
606 {
607 scsiDev.phase = BUS_BUSY;
608 }
609 }
610
611 static void process_MessageOut()
612 {
613 scsiEnterPhase(MESSAGE_OUT);
614
615 scsiDev.atnFlag = 0;
616 scsiDev.parityError = 0;
617 scsiDev.msgOut = scsiReadByte();
618 scsiDev.msgCount++;
619
620 if (scsiDev.parityError &&
621 (scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY) &&
622 (scsiDev.compatMode >= COMPAT_SCSI2))
623 {
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)
628 {
629 scsiReadByte();
630 }
631
632 // Go-back and try the message again.
633 scsiDev.atnFlag = 1;
634 scsiDev.parityError = 0;
635 }
636 else if (scsiDev.msgOut == 0x00)
637 {
638 // COMMAND COMPLETE. but why would the target be receiving this ? nfi.
639 enter_BusFree();
640 }
641 else if (scsiDev.msgOut == 0x06)
642 {
643 // ABORT
644 scsiDiskReset();
645 enter_BusFree();
646 }
647 else if (scsiDev.msgOut == 0x0C)
648 {
649 // BUS DEVICE RESET
650
651 scsiDiskReset();
652
653 scsiDev.target->unitAttention = SCSI_BUS_RESET;
654
655 // ANY initiator can reset the reservation state via this message.
656 scsiDev.target->reservedId = -1;
657 scsiDev.target->reserverId = -1;
658 enter_BusFree();
659 }
660 else if (scsiDev.msgOut == 0x05)
661 {
662 // Initiate Detected Error
663 // Ignore for now
664 }
665 else if (scsiDev.msgOut == 0x0F)
666 {
667 // INITIATE RECOVERY
668 // Ignore for now
669 }
670 else if (scsiDev.msgOut == 0x10)
671 {
672 // RELEASE RECOVERY
673 // Ignore for now
674 enter_BusFree();
675 }
676 else if (scsiDev.msgOut == MSG_REJECT)
677 {
678 // Message Reject
679 // Oh well.
680 scsiDev.resetFlag = 1;
681 }
682 else if (scsiDev.msgOut == 0x08)
683 {
684 // NOP
685 }
686 else if (scsiDev.msgOut == 0x09)
687 {
688 // Message Parity Error
689 // Go back and re-send the last message.
690 scsiDev.phase = MESSAGE_IN;
691 }
692 else if (scsiDev.msgOut & 0x80) // 0x80 -> 0xFF
693 {
694 // IDENTIFY
695 if ((scsiDev.msgOut & 0x18) || // Reserved bits set.
696 (scsiDev.msgOut & 0x20)) // We don't have any target routines!
697 {
698 messageReject();
699 }
700
701 scsiDev.lun = scsiDev.msgOut & 0x7;
702 scsiDev.discPriv =
703 ((scsiDev.msgOut & 0x40) && (scsiDev.initiatorId >= 0))
704 ? 1 : 0;
705 }
706 else if (scsiDev.msgOut >= 0x20 && scsiDev.msgOut <= 0x2F)
707 {
708 // Two byte message. We don't support these. read and discard.
709 scsiReadByte();
710
711 if (scsiDev.msgOut == 0x23) {
712 // Ignore Wide Residue. We're only 8 bit anyway.
713 } else {
714 messageReject();
715 }
716 }
717 else if (scsiDev.msgOut == 0x01)
718 {
719 int i;
720
721 // Extended message.
722 int msgLen = scsiReadByte();
723 if (msgLen == 0) msgLen = 256;
724 uint8_t extmsg[256];
725 for (i = 0; i < msgLen && !scsiDev.resetFlag; ++i)
726 {
727 // Discard bytes.
728 extmsg[i] = scsiReadByte();
729 }
730
731 if (extmsg[0] == 3 && msgLen == 2) // Wide Data Request
732 {
733 // Negotiate down to 8bit
734 scsiEnterPhase(MESSAGE_IN);
735 static const uint8_t WDTR[] = {0x01, 0x02, 0x03, 0x00};
736 scsiWrite(WDTR, sizeof(WDTR));
737 }
738 else if (extmsg[0] == 1 && msgLen == 3) // Synchronous data request
739 {
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));
744 }
745 else
746 {
747 // Not supported
748 messageReject();
749 }
750 }
751 else
752 {
753 messageReject();
754 }
755
756 // Re-check the ATN flag in case it stays asserted.
757 scsiDev.atnFlag |= scsiStatusATN();
758 }
759
760 void scsiPoll(void)
761 {
762 if (unlikely(scsiDev.resetFlag))
763 {
764 scsiReset();
765 if ((scsiDev.resetFlag = scsiStatusRST()))
766 {
767 // Still in reset phase. Do not try and process any commands.
768 return;
769 }
770 }
771
772 switch (scsiDev.phase)
773 {
774 case BUS_FREE:
775 if (scsiStatusBSY())
776 {
777 scsiDev.phase = BUS_BUSY;
778 }
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)
784 {
785 enter_SelectionPhase();
786 }
787 break;
788
789 case BUS_BUSY:
790 // Someone is using the bus. Perhaps they are trying to
791 // select us.
792 if (*SCSI_STS_SELECTED)
793 {
794 enter_SelectionPhase();
795 }
796 else if (!scsiStatusBSY())
797 {
798 scsiDev.phase = BUS_FREE;
799 }
800 break;
801
802 case ARBITRATION:
803 // TODO Support reselection.
804 break;
805
806 case SELECTION:
807 process_SelectionPhase();
808 break;
809
810 case RESELECTION:
811 // Not currently supported!
812 break;
813
814 case COMMAND:
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.
818 if (scsiDev.atnFlag)
819 {
820 process_MessageOut();
821 }
822 else
823 {
824 process_Command();
825 }
826 break;
827
828 case DATA_IN:
829 scsiDev.atnFlag |= scsiStatusATN();
830 if (scsiDev.atnFlag)
831 {
832 process_MessageOut();
833 }
834 else
835 {
836 process_DataIn();
837 }
838 break;
839
840 case DATA_OUT:
841 scsiDev.atnFlag |= scsiStatusATN();
842 if (scsiDev.atnFlag)
843 {
844 process_MessageOut();
845 }
846 else
847 {
848 process_DataOut();
849 }
850 break;
851
852 case STATUS:
853 scsiDev.atnFlag |= scsiStatusATN();
854 if (scsiDev.atnFlag)
855 {
856 process_MessageOut();
857 }
858 else
859 {
860 process_Status();
861 }
862 break;
863
864 case MESSAGE_IN:
865 scsiDev.atnFlag |= scsiStatusATN();
866 if (scsiDev.atnFlag)
867 {
868 process_MessageOut();
869 }
870 else
871 {
872 process_MessageIn();
873 }
874
875 break;
876
877 case MESSAGE_OUT:
878 process_MessageOut();
879 break;
880 }
881 }
882
883 void scsiInit()
884 {
885 static int firstInit = 1;
886
887 scsiDev.atnFlag = 0;
888 scsiDev.resetFlag = 1;
889 scsiDev.phase = BUS_FREE;
890 scsiDev.target = NULL;
891 scsiDev.compatMode = COMPAT_UNKNOWN;
892
893 int i;
894 for (i = 0; i < S2S_MAX_TARGETS; ++i)
895 {
896 const S2S_TargetCfg* cfg = s2s_getConfigByIndex(i);
897 if (cfg && (cfg->scsiId & S2S_CFG_TARGET_ENABLED))
898 {
899 scsiDev.targets[i].targetId = cfg->scsiId & S2S_CFG_TARGET_ID_BITS;
900 scsiDev.targets[i].cfg = cfg;
901
902 scsiDev.targets[i].liveCfg.bytesPerSector = cfg->bytesPerSector;
903 }
904 else
905 {
906 scsiDev.targets[i].targetId = 0xff;
907 scsiDev.targets[i].cfg = NULL;
908 }
909 scsiDev.targets[i].reservedId = -1;
910 scsiDev.targets[i].reserverId = -1;
911 if (firstInit)
912 {
913 scsiDev.targets[i].unitAttention = POWER_ON_RESET;
914 }
915 else
916 {
917 scsiDev.targets[i].unitAttention = PARAMETERS_CHANGED;
918 }
919 scsiDev.targets[i].sense.code = NO_SENSE;
920 scsiDev.targets[i].sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;
921 }
922 firstInit = 0;
923 }
924
925 /* TODO REENABLE
926 void scsiDisconnect()
927 {
928 scsiEnterPhase(MESSAGE_IN);
929 scsiWriteByte(0x02); // save data pointer
930 scsiWriteByte(0x04); // disconnect msg.
931
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;
937 enter_BusFree();
938 scsiDev.phase = phase;
939 }
940 */
941
942 /* TODO REENABLE
943 int scsiReconnect()
944 {
945 int reconnected = 0;
946
947 int sel = SCSI_ReadFilt(SCSI_Filt_SEL);
948 int bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
949 if (!sel && !bsy)
950 {
951 s2s_delay_us(1);
952 sel = SCSI_ReadFilt(SCSI_Filt_SEL);
953 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
954 }
955
956 if (!sel && !bsy)
957 {
958 // Arbitrate.
959 s2s_ledOn();
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);
964
965 s2s_delay_us(3); // arbitrate delay. 2.4us.
966
967 uint8_t dbx = scsiReadDBxPins();
968 sel = SCSI_ReadFilt(SCSI_Filt_SEL);
969 if (sel || ((dbx ^ scsiIdMask) > scsiIdMask))
970 {
971 // Lost arbitration.
972 SCSI_Out_Ctl_Write(0);
973 SCSI_ClearPin(SCSI_Out_BSY);
974 s2s_ledOff();
975 }
976 else
977 {
978 // Won arbitration
979 SCSI_SetPin(SCSI_Out_SEL);
980 s2s_delay_us(1); // Bus clear + Bus settle.
981
982 // Reselection phase
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
989
990 uint32_t waitStart_ms = getTime_ms();
991 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
992 // Wait for initiator.
993 while (
994 !bsy &&
995 !scsiDev.resetFlag &&
996 (elapsedTime_ms(waitStart_ms) < 250))
997 {
998 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);
999 }
1000
1001 if (bsy)
1002 {
1003 SCSI_SetPin(SCSI_Out_BSY);
1004 scsiDeskewDelay(); // 2 deskew delays
1005 scsiDeskewDelay(); // 2 deskew delays
1006 SCSI_ClearPin(SCSI_Out_SEL);
1007
1008 // Prepare for the initial IDENTIFY message.
1009 SCSI_Out_Ctl_Write(0);
1010 scsiEnterPhase(MESSAGE_IN);
1011
1012 // Send identify command
1013 scsiWriteByte(0x80);
1014
1015 scsiEnterPhase(scsiDev.phase);
1016 reconnected = 1;
1017 }
1018 else
1019 {
1020 // reselect timeout.
1021 SCSI_Out_Ctl_Write(0);
1022 SCSI_ClearPin(SCSI_Out_SEL);
1023 SCSI_CTL_PHASE_Write(0);
1024 s2s_ledOff();
1025 }
1026 }
1027 }
1028 return reconnected;
1029 }
1030 */
1031