Fixed broken Unit Attention Condition and reset behaviour.
[SCSI2SD-V6.git] / software / SCSI2SD / SCSI2SD.cydsn / scsi.c
1 //      Copyright (C) 2014 Michael McMaster <michael@codesrc.com>\r
2 //\r
3 //      This file is part of SCSI2SD.\r
4 //\r
5 //      SCSI2SD is free software: you can redistribute it and/or modify\r
6 //      it under the terms of the GNU General Public License as published by\r
7 //      the Free Software Foundation, either version 3 of the License, or\r
8 //      (at your option) any later version.\r
9 //\r
10 //      SCSI2SD is distributed in the hope that it will be useful,\r
11 //      but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 //      GNU General Public License for more details.\r
14 //\r
15 //      You should have received a copy of the GNU General Public License\r
16 //      along with SCSI2SD.  If not, see <http://www.gnu.org/licenses/>.\r
17 \r
18 #include "device.h"\r
19 #include "scsi.h"\r
20 #include "scsiPhy.h"\r
21 #include "config.h"\r
22 #include "bits.h"\r
23 #include "diagnostic.h"\r
24 #include "disk.h"\r
25 #include "inquiry.h"\r
26 #include "led.h"\r
27 #include "mode.h"\r
28 #include "disk.h"\r
29 \r
30 #include <string.h>\r
31 \r
32 // Global SCSI device state.\r
33 ScsiDevice scsiDev;\r
34 \r
35 static void enter_SelectionPhase(void);\r
36 static void process_SelectionPhase(void);\r
37 static void enter_BusFree(void);\r
38 static void enter_MessageIn(uint8 message);\r
39 static void process_MessageIn(void);\r
40 static void enter_Status(uint8 status);\r
41 static void process_Status(void);\r
42 static void enter_DataIn(int len);\r
43 static void process_DataIn(void);\r
44 static void process_DataOut(void);\r
45 static void process_Command(void);\r
46 \r
47 static void doReserveRelease(void);\r
48 \r
49 static void enter_BusFree()\r
50 {\r
51         // TODO MPC3000 testing.\r
52         // 1,2us: Cannot see SCSI device.\r
53         // 5us: Can see SCSI device, format fails\r
54         // 10us: Format succeeds.\r
55         // 25us: Format fails.\r
56         CyDelayUs(10);\r
57 \r
58 \r
59 \r
60 \r
61         SCSI_ClearPin(SCSI_Out_BSY);\r
62         // We now have a Bus Clear Delay of 800ns to release remaining signals.\r
63         SCSI_ClearPin(SCSI_Out_MSG);\r
64         SCSI_ClearPin(SCSI_Out_CD);\r
65         SCSI_CTL_IO_Write(0);\r
66 \r
67         // Wait for the initiator to cease driving signals\r
68         // Bus settle delay + bus clear delay = 1200ns\r
69         CyDelayUs(2);\r
70 \r
71         ledOff();\r
72         scsiDev.phase = BUS_FREE;\r
73 }\r
74 \r
75 static void enter_MessageIn(uint8 message)\r
76 {\r
77         scsiDev.msgIn = message;\r
78         scsiDev.phase = MESSAGE_IN;\r
79 }\r
80 \r
81 static void process_MessageIn()\r
82 {\r
83         scsiEnterPhase(MESSAGE_IN);\r
84         scsiWriteByte(scsiDev.msgIn);\r
85 \r
86         if (scsiDev.atnFlag)\r
87         {\r
88                 // If there was a parity error, we go\r
89                 // back to MESSAGE_OUT first, get out parity error message, then come\r
90                 // back here.\r
91         }\r
92         else /*if (scsiDev.msgIn == MSG_COMMAND_COMPLETE)*/\r
93         {\r
94                 enter_BusFree();\r
95         }\r
96 }\r
97 \r
98 static void messageReject()\r
99 {\r
100         scsiEnterPhase(MESSAGE_IN);\r
101         scsiWriteByte(MSG_REJECT);\r
102 }\r
103 \r
104 static void enter_Status(uint8 status)\r
105 {\r
106         scsiDev.status = status;\r
107         scsiDev.phase = STATUS;\r
108 \r
109 \r
110         #ifdef MM_DEBUG\r
111         scsiDev.lastStatus = scsiDev.status;\r
112         scsiDev.lastSense = scsiDev.sense.code;\r
113         #endif\r
114 }\r
115 \r
116 static void process_Status()\r
117 {\r
118         scsiEnterPhase(STATUS);\r
119         scsiWriteByte(scsiDev.status);\r
120 \r
121         #ifdef MM_DEBUG\r
122         scsiDev.lastStatus = scsiDev.status;\r
123         scsiDev.lastSense = scsiDev.sense.code;\r
124         #endif\r
125 \r
126         // Command Complete occurs AFTER a valid status has been\r
127         // sent. then we go bus-free.\r
128         enter_MessageIn(MSG_COMMAND_COMPLETE);\r
129 }\r
130 \r
131 static void enter_DataIn(int len)\r
132 {\r
133         scsiDev.dataLen = len;\r
134         scsiDev.phase = DATA_IN;\r
135 }\r
136 \r
137 static void process_DataIn()\r
138 {\r
139         uint32 len;\r
140 \r
141         if (scsiDev.dataLen > sizeof(scsiDev.data))\r
142         {\r
143                 scsiDev.dataLen = sizeof(scsiDev.data);\r
144         }\r
145 \r
146         len = scsiDev.dataLen - scsiDev.dataPtr;\r
147         if (len > 0)\r
148         {\r
149                 scsiEnterPhase(DATA_IN);\r
150                 scsiWrite(scsiDev.data + scsiDev.dataPtr, len);\r
151                 scsiDev.dataPtr += len;\r
152         }\r
153 \r
154         if ((scsiDev.dataPtr >= scsiDev.dataLen) &&\r
155                 (transfer.currentBlock == transfer.blocks))\r
156         {\r
157                 enter_Status(GOOD);\r
158         }\r
159 }\r
160 \r
161 static void process_DataOut()\r
162 {\r
163         uint32 len;\r
164 \r
165         if (scsiDev.dataLen > sizeof(scsiDev.data))\r
166         {\r
167                 scsiDev.dataLen = sizeof(scsiDev.data);\r
168         }\r
169 \r
170         scsiDev.parityError = 0;\r
171         len = scsiDev.dataLen - scsiDev.dataPtr;\r
172         if (len > 0)\r
173         {\r
174                 scsiEnterPhase(DATA_OUT);\r
175 \r
176                 scsiRead(scsiDev.data + scsiDev.dataPtr, len);\r
177                 scsiDev.dataPtr += len;\r
178 \r
179                 // TODO re-implement parity checking\r
180                 if (0 && scsiDev.parityError && config->enableParity)\r
181                 {\r
182                         scsiDev.sense.code = ABORTED_COMMAND;\r
183                         scsiDev.sense.asc = SCSI_PARITY_ERROR;\r
184                         enter_Status(CHECK_CONDITION);\r
185                 }\r
186         }\r
187 \r
188         if ((scsiDev.dataPtr >= scsiDev.dataLen) &&\r
189                 (transfer.currentBlock == transfer.blocks))\r
190         {\r
191                 enter_Status(GOOD);\r
192         }\r
193 }\r
194 \r
195 static const uint8 CmdGroupBytes[8] = {6, 10, 10, 6, 6, 12, 6, 6};\r
196 static void process_Command()\r
197 {\r
198         int group;\r
199         int cmdSize;\r
200         uint8 command;\r
201         uint8 lun;\r
202 \r
203         scsiEnterPhase(COMMAND);\r
204         scsiDev.parityError = 0;\r
205 \r
206         memset(scsiDev.cdb, 0, sizeof(scsiDev.cdb));\r
207         scsiDev.cdb[0] = scsiReadByte();\r
208 \r
209         group = scsiDev.cdb[0] >> 5;\r
210         cmdSize = CmdGroupBytes[group];\r
211         scsiRead(scsiDev.cdb + 1, cmdSize - 1);\r
212 \r
213         command = scsiDev.cdb[0];\r
214         lun = scsiDev.cdb[1] >> 5;\r
215 \r
216         #ifdef MM_DEBUG\r
217         scsiDev.cmdCount++;\r
218         #endif\r
219 \r
220         if (scsiDev.resetFlag)\r
221         {\r
222 #ifdef MM_DEBUG\r
223                 // Don't log bogus commands\r
224                 scsiDev.cmdCount--;\r
225                 memset(scsiDev.cdb, 0xff, sizeof(scsiDev.cdb));\r
226 #endif\r
227                 return;\r
228         }\r
229         else if (scsiDev.parityError)\r
230         {\r
231                 scsiDev.sense.code = ABORTED_COMMAND;\r
232                 scsiDev.sense.asc = SCSI_PARITY_ERROR;\r
233                 enter_Status(CHECK_CONDITION);\r
234         }\r
235         else if (command == 0x12)\r
236         {\r
237                 scsiInquiry();\r
238         }\r
239         else if (command == 0x03)\r
240         {\r
241                 // REQUEST SENSE\r
242                 uint32 allocLength = scsiDev.cdb[4];\r
243                 if (allocLength == 0) allocLength = 256;\r
244                 memset(scsiDev.data, 0, 18);\r
245                 scsiDev.data[0] = 0xF0;\r
246                 scsiDev.data[2] = scsiDev.sense.code & 0x0F;\r
247 \r
248                 scsiDev.data[3] = transfer.lba >> 24;\r
249                 scsiDev.data[4] = transfer.lba >> 16;\r
250                 scsiDev.data[5] = transfer.lba >> 8;\r
251                 scsiDev.data[6] = transfer.lba;\r
252 \r
253                 // Additional bytes if there are errors to report\r
254                 int responseLength;\r
255                 if (scsiDev.sense.code == NO_SENSE)\r
256                 {\r
257                         responseLength = 8;\r
258                 }\r
259                 else\r
260                 {\r
261                         responseLength = 18;\r
262                         scsiDev.data[7] = 10; // additional length\r
263                         scsiDev.data[12] = scsiDev.sense.asc >> 8;\r
264                         scsiDev.data[13] = scsiDev.sense.asc;\r
265                 }\r
266 \r
267                 // Silently truncate results. SCSI-2 spec 8.2.14.\r
268                 enter_DataIn(\r
269                         (allocLength < responseLength) ? allocLength : responseLength\r
270                         );\r
271 \r
272                 // This is a good time to clear out old sense information.\r
273                 scsiDev.sense.code = NO_SENSE;\r
274                 scsiDev.sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;\r
275         }\r
276         // Some old SCSI drivers do NOT properly support\r
277         // unitAttention. eg. the Mac Plus would trigger a SCSI reset\r
278         // on receiving the unit attention response on boot, thus\r
279         // triggering another unit attention condition.\r
280         else if (scsiDev.unitAttention && config->enableUnitAttention)\r
281         {\r
282                 scsiDev.sense.code = UNIT_ATTENTION;\r
283                 scsiDev.sense.asc = scsiDev.unitAttention;\r
284 \r
285                 // If initiator doesn't do REQUEST SENSE for the next command, then\r
286                 // data is lost.\r
287                 scsiDev.unitAttention = 0;\r
288 \r
289                 enter_Status(CHECK_CONDITION);\r
290         }\r
291         else if (lun)\r
292         {\r
293                 scsiDev.sense.code = ILLEGAL_REQUEST;\r
294                 scsiDev.sense.asc = LOGICAL_UNIT_NOT_SUPPORTED;\r
295                 enter_Status(CHECK_CONDITION);\r
296         }\r
297         else if (command == 0x17 || command == 0x16)\r
298         {\r
299                 doReserveRelease();\r
300         }\r
301         else if ((scsiDev.reservedId >= 0) &&\r
302                 (scsiDev.reservedId != scsiDev.initiatorId))\r
303         {\r
304                 enter_Status(CONFLICT);\r
305         }\r
306         else if (command == 0x1C)\r
307         {\r
308                 scsiReceiveDiagnostic();\r
309         }\r
310         else if (command == 0x1D)\r
311         {\r
312                 scsiSendDiagnostic();\r
313         }\r
314         else if (\r
315                 !scsiModeCommand() &&\r
316                 !scsiDiskCommand())\r
317         {\r
318                 scsiDev.sense.code = ILLEGAL_REQUEST;\r
319                 scsiDev.sense.asc = INVALID_COMMAND_OPERATION_CODE;\r
320                 enter_Status(CHECK_CONDITION);\r
321         }\r
322 \r
323         // Successful\r
324         if (scsiDev.phase == COMMAND) // No status set, and not in DATA_IN\r
325         {\r
326                 enter_Status(GOOD);\r
327         }\r
328 \r
329 }\r
330 \r
331 static void doReserveRelease()\r
332 {\r
333         int extentReservation = scsiDev.cdb[1] & 1;\r
334         int thirdPty = scsiDev.cdb[1] & 0x10;\r
335         int thirdPtyId = (scsiDev.cdb[1] >> 1) & 0x7;\r
336         uint8 command = scsiDev.cdb[0];\r
337 \r
338         int canRelease =\r
339                 (!thirdPty && (scsiDev.initiatorId == scsiDev.reservedId)) ||\r
340                         (thirdPty &&\r
341                                 (scsiDev.reserverId == scsiDev.initiatorId) &&\r
342                                 (scsiDev.reservedId == thirdPtyId)\r
343                         );\r
344 \r
345         if (extentReservation)\r
346         {\r
347                 // Not supported.\r
348                 scsiDev.sense.code = ILLEGAL_REQUEST;\r
349                 scsiDev.sense.asc = INVALID_FIELD_IN_CDB;\r
350                 enter_Status(CHECK_CONDITION);\r
351         }\r
352         else if (command == 0x17) // release\r
353         {\r
354                 if ((scsiDev.reservedId < 0) || canRelease)\r
355                 {\r
356                         scsiDev.reservedId = -1;\r
357                         scsiDev.reserverId = -1;\r
358                 }\r
359                 else\r
360                 {\r
361                         enter_Status(CONFLICT);\r
362                 }\r
363         }\r
364         else // assume reserve.\r
365         {\r
366                 if ((scsiDev.reservedId < 0) || canRelease)\r
367                 {\r
368                         scsiDev.reserverId = scsiDev.initiatorId;\r
369                         if (thirdPty)\r
370                         {\r
371                                 scsiDev.reservedId = thirdPtyId;\r
372                         }\r
373                         else\r
374                         {\r
375                                 scsiDev.reservedId = scsiDev.initiatorId;\r
376                         }\r
377                 }\r
378                 else\r
379                 {\r
380                         // Already reserved by someone else!\r
381                         enter_Status(CONFLICT);\r
382                 }\r
383         }\r
384 }\r
385 \r
386 static void scsiReset()\r
387 {\r
388 #ifdef MM_DEBUG\r
389         scsiDev.rstCount++;\r
390 #endif\r
391         ledOff();\r
392 \r
393         scsiPhyReset();\r
394 \r
395         scsiDev.parityError = 0;\r
396         scsiDev.phase = BUS_FREE;\r
397         scsiDev.atnFlag = 0;\r
398         scsiDev.resetFlag = 0;\r
399 \r
400         if (scsiDev.unitAttention != POWER_ON_RESET)\r
401         {\r
402                 scsiDev.unitAttention = SCSI_BUS_RESET;\r
403         }\r
404         scsiDev.reservedId = -1;\r
405         scsiDev.reserverId = -1;\r
406         scsiDev.sense.code = NO_SENSE;\r
407         scsiDev.sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;\r
408         scsiDiskReset();\r
409 \r
410         // Sleep to allow the bus to settle down a bit.\r
411         // We must be ready again within the "Reset to selection time" of\r
412         // 250ms.\r
413         // There is no guarantee that the RST line will be negated by then.\r
414         // NOTE: We could be connected and powered by USB for configuration,\r
415         // in which case TERMPWR cannot be supplied, and reset will ALWAYS\r
416         // be true.\r
417         CyDelay(10); // 10ms.\r
418 }\r
419 \r
420 static void enter_SelectionPhase()\r
421 {\r
422         // Ignore stale versions of this flag, but ensure we know the\r
423         // current value if the flag is still set.\r
424         scsiDev.atnFlag = 0;\r
425         scsiDev.parityError = 0;\r
426         scsiDev.dataPtr = 0;\r
427         scsiDev.savedDataPtr = 0;\r
428         scsiDev.dataLen = 0;\r
429         scsiDev.status = GOOD;\r
430         scsiDev.phase = SELECTION;\r
431 \r
432         transfer.blocks = 0;\r
433         transfer.currentBlock = 0;\r
434 }\r
435 \r
436 static void process_SelectionPhase()\r
437 {\r
438         int sel = SCSI_ReadPin(SCSI_In_SEL);\r
439         int bsy = SCSI_ReadPin(SCSI_In_BSY);\r
440 \r
441         // Only read these pins AFTER SEL and BSY - we don't want to catch them\r
442         // during a transition period.\r
443         uint8 mask = scsiReadDBxPins();\r
444         int maskBitCount = countBits(mask);\r
445         int goodParity = (Lookup_OddParity[mask] == SCSI_ReadPin(SCSI_In_DBP));\r
446 \r
447         if (!bsy && sel &&\r
448                 (mask & scsiDev.scsiIdMask) &&\r
449                 (goodParity || !config->enableParity) && (maskBitCount <= 2))\r
450         {\r
451                 // Do we enter MESSAGE OUT immediately ? SCSI 1 and 2 standards says\r
452                 // move to MESSAGE OUT if ATN is true before we release BSY.\r
453                 // The initiate should assert ATN with SEL.\r
454                 scsiDev.atnFlag = SCSI_ReadPin(SCSI_ATN_INT);\r
455 \r
456                 // We've been selected!\r
457                 // Assert BSY - Selection success!\r
458                 // must happen within 200us (Selection abort time) of seeing our\r
459                 // ID + SEL.\r
460                 // (Note: the initiator will be waiting the "Selection time-out delay"\r
461                 // for our BSY response, which is actually a very generous 250ms)\r
462                 SCSI_SetPin(SCSI_Out_BSY);\r
463                 ledOn();\r
464 \r
465                 #ifdef MM_DEBUG\r
466                 scsiDev.selCount++;\r
467                 #endif\r
468 \r
469                 // Wait until the end of the selection phase.\r
470                 while (!scsiDev.resetFlag)\r
471                 {\r
472                         if (!SCSI_ReadPin(SCSI_In_SEL))\r
473                         {\r
474                                 break;\r
475                         }\r
476                 }\r
477 \r
478                 // Save our initiator now that we're no longer in a time-critical\r
479                 // section.\r
480                 // SCSI1/SASI initiators may not set their own ID.\r
481                 if (maskBitCount == 2)\r
482                 {\r
483                         int i;\r
484                         uint8 initiatorMask = mask ^ scsiDev.scsiIdMask;\r
485                         scsiDev.initiatorId = 0;\r
486                         for (i = 0; i < 8; ++i)\r
487                         {\r
488                                 if (initiatorMask & (1 << i))\r
489                                 {\r
490                                         scsiDev.initiatorId = i;\r
491                                         break;\r
492                                 }\r
493                         }\r
494                 }\r
495                 else\r
496                 {\r
497                         scsiDev.initiatorId = -1;\r
498                 }\r
499 \r
500                 scsiDev.phase = COMMAND;\r
501         }\r
502         else if (!sel)\r
503         {\r
504                 scsiDev.phase = BUS_BUSY;\r
505         }\r
506 }\r
507 \r
508 static void process_MessageOut()\r
509 {\r
510         scsiEnterPhase(MESSAGE_OUT);\r
511 \r
512         scsiDev.atnFlag = 0;\r
513         scsiDev.parityError = 0;\r
514         scsiDev.msgOut = scsiReadByte();\r
515 #ifdef MM_DEBUG\r
516         scsiDev.msgCount++;\r
517 #endif\r
518 \r
519         if (scsiDev.parityError)\r
520         {\r
521                 // Skip the remaining message bytes, and then start the MESSAGE_OUT\r
522                 // phase again from the start. The initiator will re-send the\r
523                 // same set of messages.\r
524                 while (SCSI_ReadPin(SCSI_ATN_INT) && !scsiDev.resetFlag)\r
525                 {\r
526                         scsiReadByte();\r
527                 }\r
528 \r
529                 // Go-back and try the message again.\r
530                 scsiDev.atnFlag = 1;\r
531                 scsiDev.parityError = 0;\r
532         }\r
533         else if (scsiDev.msgOut == 0x00)\r
534         {\r
535                 // COMMAND COMPLETE. but why would the target be receiving this ? nfi.\r
536                 enter_BusFree();\r
537         }\r
538         else if (scsiDev.msgOut == 0x06)\r
539         {\r
540                 // ABORT\r
541                 scsiDiskReset();\r
542                 enter_BusFree();\r
543         }\r
544         else if (scsiDev.msgOut == 0x0C)\r
545         {\r
546                 // BUS DEVICE RESET\r
547 \r
548                 scsiDiskReset();\r
549 \r
550                 scsiDev.unitAttention = SCSI_BUS_RESET;\r
551 \r
552                 // ANY initiator can reset the reservation state via this message.\r
553                 scsiDev.reservedId = -1;\r
554                 scsiDev.reserverId = -1;\r
555                 enter_BusFree();\r
556         }\r
557         else if (scsiDev.msgOut == 0x05)\r
558         {\r
559                 // Initiate Detected Error\r
560                 // Ignore for now\r
561         }\r
562         else if (scsiDev.msgOut == 0x0F)\r
563         {\r
564                 // INITIATE RECOVERY\r
565                 // Ignore for now\r
566         }\r
567         else if (scsiDev.msgOut == 0x10)\r
568         {\r
569                 // RELEASE RECOVERY\r
570                 // Ignore for now\r
571                 enter_BusFree();\r
572         }\r
573         else if (scsiDev.msgOut == MSG_REJECT)\r
574         {\r
575                 // Message Reject\r
576                 // Oh well.\r
577                 scsiDev.resetFlag = 1;\r
578         }\r
579         else if (scsiDev.msgOut == 0x08)\r
580         {\r
581                 // NOP\r
582         }\r
583         else if (scsiDev.msgOut == 0x09)\r
584         {\r
585                 // Message Parity Error\r
586                 // Go back and re-send the last message.\r
587                 scsiDev.phase = MESSAGE_IN;\r
588         }\r
589         else if (scsiDev.msgOut & 0x80) // 0x80 -> 0xFF\r
590         {\r
591                 // IDENTIFY\r
592                 // We don't disconnect, so ignore disconnect privilege.\r
593                 if ((scsiDev.msgOut & 0x18) || // Reserved bits set.\r
594                         (scsiDev.msgOut & 0x20)  || // We don't have any target routines!\r
595                         (scsiDev.msgOut & 0x7) // We only support LUN 0!\r
596                         )\r
597                 {\r
598                         messageReject();\r
599                 }\r
600         }\r
601         else if (scsiDev.msgOut >= 0x20 && scsiDev.msgOut <= 0x2F)\r
602         {\r
603                 // Two byte message. We don't support these. read and discard.\r
604                 scsiReadByte();\r
605         }\r
606         else if (scsiDev.msgOut == 0x01)\r
607         {\r
608                 int i;\r
609 \r
610                 // Extended message.\r
611                 int msgLen = scsiReadByte();\r
612                 if (msgLen == 0) msgLen = 256;\r
613                 for (i = 0; i < msgLen && !scsiDev.resetFlag; ++i)\r
614                 {\r
615                         // Discard bytes.\r
616                         scsiReadByte();\r
617                 }\r
618 \r
619                 // We don't support ANY extended messages.\r
620                 // Modify Data Pointer:  We don't support reselection.\r
621                 // Wide Data Transfer Request: No. 8bit only.\r
622                 // Synchronous data transfer request. No, we can't do that.\r
623                 // We don't support any 2-byte messages either.\r
624                 // And we don't support any optional 1-byte messages.\r
625                 // In each case, the correct response is MESSAGE REJECT.\r
626                 messageReject();\r
627         }\r
628         else\r
629         {\r
630                 messageReject();\r
631         }\r
632 \r
633         // Re-check the ATN flag in case it stays asserted.\r
634         scsiDev.atnFlag |= SCSI_ReadPin(SCSI_ATN_INT);\r
635 }\r
636 \r
637 void scsiPoll(void)\r
638 {\r
639         if (scsiDev.resetFlag)\r
640         {\r
641                 scsiReset();\r
642                 if ((scsiDev.resetFlag = SCSI_ReadPin(SCSI_RST_INT)))\r
643                 {\r
644                         // Still in reset phase. Do not try and process any commands.\r
645                         return;\r
646                 }\r
647         }\r
648 \r
649         switch (scsiDev.phase)\r
650         {\r
651         case BUS_FREE:\r
652                 if (SCSI_ReadPin(SCSI_In_BSY))\r
653                 {\r
654                         scsiDev.phase = BUS_BUSY;\r
655                 }\r
656                 // The Arbitration phase is optional for SCSI1/SASI hosts if there is only\r
657                 // one initiator in the chain. Support this by moving\r
658                 // straight to selection if SEL is asserted.\r
659                 // ie. the initiator won't assert BSY and it's own ID before moving to selection.\r
660                 else if (SCSI_ReadPin(SCSI_In_SEL))\r
661                 {\r
662                         enter_SelectionPhase();\r
663                 }\r
664         break;\r
665 \r
666         case BUS_BUSY:\r
667                 // Someone is using the bus. Perhaps they are trying to\r
668                 // select us.\r
669                 if (SCSI_ReadPin(SCSI_In_SEL))\r
670                 {\r
671                         enter_SelectionPhase();\r
672                 }\r
673                 else if (!SCSI_ReadPin(SCSI_In_BSY))\r
674                 {\r
675                         scsiDev.phase = BUS_FREE;\r
676                 }\r
677         break;\r
678 \r
679         case ARBITRATION:\r
680                 // TODO Support reselection.\r
681                 break;\r
682 \r
683         case SELECTION:\r
684                 process_SelectionPhase();\r
685         break;\r
686 \r
687         case RESELECTION:\r
688                 // Not currently supported!\r
689         break;\r
690 \r
691         case COMMAND:\r
692                 // Do not check ATN here. SCSI 1 & 2 initiators must set ATN\r
693                 // and SEL together upon entering the selection phase if they\r
694                 // want to send a message (IDENTIFY) immediately.\r
695                 if (scsiDev.atnFlag)\r
696                 {\r
697                         process_MessageOut();\r
698                 }\r
699                 else\r
700                 {\r
701                         process_Command();\r
702                 }\r
703         break;\r
704 \r
705         case DATA_IN:\r
706                 scsiDev.atnFlag |= SCSI_ReadPin(SCSI_ATN_INT);\r
707                 if (scsiDev.atnFlag)\r
708                 {\r
709                         process_MessageOut();\r
710                 }\r
711                 else\r
712                 {\r
713                         process_DataIn();\r
714                 }\r
715         break;\r
716 \r
717         case DATA_OUT:\r
718                 scsiDev.atnFlag |= SCSI_ReadPin(SCSI_ATN_INT);\r
719                 if (scsiDev.atnFlag)\r
720                 {\r
721                         process_MessageOut();\r
722                 }\r
723                 else\r
724                 {\r
725                         process_DataOut();\r
726                 }\r
727         break;\r
728 \r
729         case STATUS:\r
730                 scsiDev.atnFlag |= SCSI_ReadPin(SCSI_ATN_INT);\r
731                 if (scsiDev.atnFlag)\r
732                 {\r
733                         process_MessageOut();\r
734                 }\r
735                 else\r
736                 {\r
737                         process_Status();\r
738                 }\r
739         break;\r
740 \r
741         case MESSAGE_IN:\r
742                 scsiDev.atnFlag |= SCSI_ReadPin(SCSI_ATN_INT);\r
743                 if (scsiDev.atnFlag)\r
744                 {\r
745                         process_MessageOut();\r
746                 }\r
747                 else\r
748                 {\r
749                         process_MessageIn();\r
750                 }\r
751 \r
752         break;\r
753 \r
754         case MESSAGE_OUT:\r
755                 process_MessageOut();\r
756         break;\r
757         }\r
758 }\r
759 \r
760 void scsiInit()\r
761 {\r
762         scsiDev.scsiIdMask = 1 << (config->scsiId);\r
763 \r
764         scsiDev.atnFlag = 0;\r
765         scsiDev.resetFlag = 1;\r
766         scsiDev.phase = BUS_FREE;\r
767         scsiDev.reservedId = -1;\r
768         scsiDev.reserverId = -1;\r
769         scsiDev.unitAttention = POWER_ON_RESET;\r
770 }\r
771 \r