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