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