Adding Ensoniq ASR-10 support and improved FORMAT UNIT command.
[SCSI2SD-V6.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 \r
285                 // As specified by the SASI and SCSI1 standard.\r
286                 // Newer initiators won't be specifying 0 anyway.\r
287                 if (allocLength == 0) allocLength = 4;\r
288 \r
289                 memset(scsiDev.data, 0, 256); // Max possible alloc length\r
290                 scsiDev.data[0] = 0xF0;\r
291                 scsiDev.data[2] = scsiDev.sense.code & 0x0F;\r
292 \r
293                 scsiDev.data[3] = transfer.lba >> 24;\r
294                 scsiDev.data[4] = transfer.lba >> 16;\r
295                 scsiDev.data[5] = transfer.lba >> 8;\r
296                 scsiDev.data[6] = transfer.lba;\r
297 \r
298                 // Additional bytes if there are errors to report\r
299                 scsiDev.data[7] = 10; // additional length\r
300                 scsiDev.data[12] = scsiDev.sense.asc >> 8;\r
301                 scsiDev.data[13] = scsiDev.sense.asc;\r
302 \r
303                 // Silently truncate results. SCSI-2 spec 8.2.14.\r
304                 enter_DataIn(allocLength);\r
305 \r
306                 // This is a good time to clear out old sense information.\r
307                 scsiDev.sense.code = NO_SENSE;\r
308                 scsiDev.sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;\r
309         }\r
310         // Some old SCSI drivers do NOT properly support\r
311         // unitAttention. eg. the Mac Plus would trigger a SCSI reset\r
312         // on receiving the unit attention response on boot, thus\r
313         // triggering another unit attention condition.\r
314         else if (scsiDev.unitAttention && config->enableUnitAttention)\r
315         {\r
316                 scsiDev.sense.code = UNIT_ATTENTION;\r
317                 scsiDev.sense.asc = scsiDev.unitAttention;\r
318 \r
319                 // If initiator doesn't do REQUEST SENSE for the next command, then\r
320                 // data is lost.\r
321                 scsiDev.unitAttention = 0;\r
322 \r
323                 enter_Status(CHECK_CONDITION);\r
324         }\r
325         else if (lun)\r
326         {\r
327                 scsiDev.sense.code = ILLEGAL_REQUEST;\r
328                 scsiDev.sense.asc = LOGICAL_UNIT_NOT_SUPPORTED;\r
329                 enter_Status(CHECK_CONDITION);\r
330         }\r
331         else if (command == 0x17 || command == 0x16)\r
332         {\r
333                 doReserveRelease();\r
334         }\r
335         else if ((scsiDev.reservedId >= 0) &&\r
336                 (scsiDev.reservedId != scsiDev.initiatorId))\r
337         {\r
338                 enter_Status(CONFLICT);\r
339         }\r
340         else if (command == 0x1C)\r
341         {\r
342                 scsiReceiveDiagnostic();\r
343         }\r
344         else if (command == 0x1D)\r
345         {\r
346                 scsiSendDiagnostic();\r
347         }\r
348         else if (\r
349                 !scsiModeCommand() &&\r
350                 !scsiDiskCommand())\r
351         {\r
352                 scsiDev.sense.code = ILLEGAL_REQUEST;\r
353                 scsiDev.sense.asc = INVALID_COMMAND_OPERATION_CODE;\r
354                 enter_Status(CHECK_CONDITION);\r
355         }\r
356 \r
357         // Successful\r
358         if (scsiDev.phase == COMMAND) // No status set, and not in DATA_IN\r
359         {\r
360                 enter_Status(GOOD);\r
361         }\r
362 \r
363 }\r
364 \r
365 static void doReserveRelease()\r
366 {\r
367         int extentReservation = scsiDev.cdb[1] & 1;\r
368         int thirdPty = scsiDev.cdb[1] & 0x10;\r
369         int thirdPtyId = (scsiDev.cdb[1] >> 1) & 0x7;\r
370         uint8 command = scsiDev.cdb[0];\r
371 \r
372         int canRelease =\r
373                 (!thirdPty && (scsiDev.initiatorId == scsiDev.reservedId)) ||\r
374                         (thirdPty &&\r
375                                 (scsiDev.reserverId == scsiDev.initiatorId) &&\r
376                                 (scsiDev.reservedId == thirdPtyId)\r
377                         );\r
378 \r
379         if (extentReservation)\r
380         {\r
381                 // Not supported.\r
382                 scsiDev.sense.code = ILLEGAL_REQUEST;\r
383                 scsiDev.sense.asc = INVALID_FIELD_IN_CDB;\r
384                 enter_Status(CHECK_CONDITION);\r
385         }\r
386         else if (command == 0x17) // release\r
387         {\r
388                 if ((scsiDev.reservedId < 0) || canRelease)\r
389                 {\r
390                         scsiDev.reservedId = -1;\r
391                         scsiDev.reserverId = -1;\r
392                 }\r
393                 else\r
394                 {\r
395                         enter_Status(CONFLICT);\r
396                 }\r
397         }\r
398         else // assume reserve.\r
399         {\r
400                 if ((scsiDev.reservedId < 0) || canRelease)\r
401                 {\r
402                         scsiDev.reserverId = scsiDev.initiatorId;\r
403                         if (thirdPty)\r
404                         {\r
405                                 scsiDev.reservedId = thirdPtyId;\r
406                         }\r
407                         else\r
408                         {\r
409                                 scsiDev.reservedId = scsiDev.initiatorId;\r
410                         }\r
411                 }\r
412                 else\r
413                 {\r
414                         // Already reserved by someone else!\r
415                         enter_Status(CONFLICT);\r
416                 }\r
417         }\r
418 }\r
419 \r
420 static void scsiReset()\r
421 {\r
422 #ifdef MM_DEBUG\r
423         scsiDev.rstCount++;\r
424 #endif\r
425         ledOff();\r
426 \r
427         scsiPhyReset();\r
428 \r
429         scsiDev.parityError = 0;\r
430         scsiDev.phase = BUS_FREE;\r
431         scsiDev.atnFlag = 0;\r
432         scsiDev.resetFlag = 0;\r
433 \r
434         if (scsiDev.unitAttention != POWER_ON_RESET)\r
435         {\r
436                 scsiDev.unitAttention = SCSI_BUS_RESET;\r
437         }\r
438         scsiDev.reservedId = -1;\r
439         scsiDev.reserverId = -1;\r
440         scsiDev.sense.code = NO_SENSE;\r
441         scsiDev.sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;\r
442         scsiDiskReset();\r
443 \r
444         scsiDev.postDataOutHook = NULL;\r
445 \r
446         // Sleep to allow the bus to settle down a bit.\r
447         // We must be ready again within the "Reset to selection time" of\r
448         // 250ms.\r
449         // There is no guarantee that the RST line will be negated by then.\r
450         // NOTE: We could be connected and powered by USB for configuration,\r
451         // in which case TERMPWR cannot be supplied, and reset will ALWAYS\r
452         // be true.\r
453         CyDelay(10); // 10ms.\r
454 }\r
455 \r
456 static void enter_SelectionPhase()\r
457 {\r
458         // Ignore stale versions of this flag, but ensure we know the\r
459         // current value if the flag is still set.\r
460         scsiDev.atnFlag = 0;\r
461         scsiDev.parityError = 0;\r
462         scsiDev.dataPtr = 0;\r
463         scsiDev.savedDataPtr = 0;\r
464         scsiDev.dataLen = 0;\r
465         scsiDev.status = GOOD;\r
466         scsiDev.phase = SELECTION;\r
467 \r
468         transfer.blocks = 0;\r
469         transfer.currentBlock = 0;\r
470 \r
471         scsiDev.postDataOutHook = NULL;\r
472 }\r
473 \r
474 static void process_SelectionPhase()\r
475 {\r
476         int sel = SCSI_ReadPin(SCSI_In_SEL);\r
477         int bsy = SCSI_ReadPin(SCSI_In_BSY);\r
478 \r
479         // Only read these pins AFTER SEL and BSY - we don't want to catch them\r
480         // during a transition period.\r
481         uint8 mask = scsiReadDBxPins();\r
482         int maskBitCount = countBits(mask);\r
483         int goodParity = (Lookup_OddParity[mask] == SCSI_ReadPin(SCSI_In_DBP));\r
484 \r
485         if (!bsy && sel &&\r
486                 (mask & scsiDev.scsiIdMask) &&\r
487                 (goodParity || !config->enableParity) && (maskBitCount <= 2))\r
488         {\r
489                 // Do we enter MESSAGE OUT immediately ? SCSI 1 and 2 standards says\r
490                 // move to MESSAGE OUT if ATN is true before we assert BSY.\r
491                 // The initiator should assert ATN with SEL.\r
492                 scsiDev.atnFlag = SCSI_ReadPin(SCSI_ATN_INT);\r
493                 \r
494                 // Unit attention breaks many older SCSI hosts. Disable it completely for\r
495                 // SCSI-1 (and older) hosts, regardless of our configured setting.\r
496                 if (!scsiDev.atnFlag)\r
497                 {\r
498                         scsiDev.unitAttention = 0;\r
499                 }\r
500 \r
501                 // We've been selected!\r
502                 // Assert BSY - Selection success!\r
503                 // must happen within 200us (Selection abort time) of seeing our\r
504                 // ID + SEL.\r
505                 // (Note: the initiator will be waiting the "Selection time-out delay"\r
506                 // for our BSY response, which is actually a very generous 250ms)\r
507                 SCSI_SetPin(SCSI_Out_BSY);\r
508                 ledOn();\r
509 \r
510                 #ifdef MM_DEBUG\r
511                 scsiDev.selCount++;\r
512                 #endif\r
513 \r
514                 // Wait until the end of the selection phase.\r
515                 while (!scsiDev.resetFlag)\r
516                 {\r
517                         if (!SCSI_ReadPin(SCSI_In_SEL))\r
518                         {\r
519                                 break;\r
520                         }\r
521                 }\r
522 \r
523                 // Save our initiator now that we're no longer in a time-critical\r
524                 // section.\r
525                 // SCSI1/SASI initiators may not set their own ID.\r
526                 if (maskBitCount == 2)\r
527                 {\r
528                         int i;\r
529                         uint8 initiatorMask = mask ^ scsiDev.scsiIdMask;\r
530                         scsiDev.initiatorId = 0;\r
531                         for (i = 0; i < 8; ++i)\r
532                         {\r
533                                 if (initiatorMask & (1 << i))\r
534                                 {\r
535                                         scsiDev.initiatorId = i;\r
536                                         break;\r
537                                 }\r
538                         }\r
539                 }\r
540                 else\r
541                 {\r
542                         scsiDev.initiatorId = -1;\r
543                 }\r
544 \r
545                 scsiDev.phase = COMMAND;\r
546         }\r
547         else if (!sel)\r
548         {\r
549                 scsiDev.phase = BUS_BUSY;\r
550         }\r
551 }\r
552 \r
553 static void process_MessageOut()\r
554 {\r
555         scsiEnterPhase(MESSAGE_OUT);\r
556 \r
557         scsiDev.atnFlag = 0;\r
558         scsiDev.parityError = 0;\r
559         scsiDev.msgOut = scsiReadByte();\r
560 #ifdef MM_DEBUG\r
561         scsiDev.msgCount++;\r
562 #endif\r
563 \r
564         if (scsiDev.parityError)\r
565         {\r
566                 // Skip the remaining message bytes, and then start the MESSAGE_OUT\r
567                 // phase again from the start. The initiator will re-send the\r
568                 // same set of messages.\r
569                 while (SCSI_ReadPin(SCSI_ATN_INT) && !scsiDev.resetFlag)\r
570                 {\r
571                         scsiReadByte();\r
572                 }\r
573 \r
574                 // Go-back and try the message again.\r
575                 scsiDev.atnFlag = 1;\r
576                 scsiDev.parityError = 0;\r
577         }\r
578         else if (scsiDev.msgOut == 0x00)\r
579         {\r
580                 // COMMAND COMPLETE. but why would the target be receiving this ? nfi.\r
581                 enter_BusFree();\r
582         }\r
583         else if (scsiDev.msgOut == 0x06)\r
584         {\r
585                 // ABORT\r
586                 scsiDiskReset();\r
587                 enter_BusFree();\r
588         }\r
589         else if (scsiDev.msgOut == 0x0C)\r
590         {\r
591                 // BUS DEVICE RESET\r
592 \r
593                 scsiDiskReset();\r
594 \r
595                 scsiDev.unitAttention = SCSI_BUS_RESET;\r
596 \r
597                 // ANY initiator can reset the reservation state via this message.\r
598                 scsiDev.reservedId = -1;\r
599                 scsiDev.reserverId = -1;\r
600                 enter_BusFree();\r
601         }\r
602         else if (scsiDev.msgOut == 0x05)\r
603         {\r
604                 // Initiate Detected Error\r
605                 // Ignore for now\r
606         }\r
607         else if (scsiDev.msgOut == 0x0F)\r
608         {\r
609                 // INITIATE RECOVERY\r
610                 // Ignore for now\r
611         }\r
612         else if (scsiDev.msgOut == 0x10)\r
613         {\r
614                 // RELEASE RECOVERY\r
615                 // Ignore for now\r
616                 enter_BusFree();\r
617         }\r
618         else if (scsiDev.msgOut == MSG_REJECT)\r
619         {\r
620                 // Message Reject\r
621                 // Oh well.\r
622                 scsiDev.resetFlag = 1;\r
623         }\r
624         else if (scsiDev.msgOut == 0x08)\r
625         {\r
626                 // NOP\r
627         }\r
628         else if (scsiDev.msgOut == 0x09)\r
629         {\r
630                 // Message Parity Error\r
631                 // Go back and re-send the last message.\r
632                 scsiDev.phase = MESSAGE_IN;\r
633         }\r
634         else if (scsiDev.msgOut & 0x80) // 0x80 -> 0xFF\r
635         {\r
636                 // IDENTIFY\r
637                 // We don't disconnect, so ignore disconnect privilege.\r
638                 if ((scsiDev.msgOut & 0x18) || // Reserved bits set.\r
639                         (scsiDev.msgOut & 0x20)  || // We don't have any target routines!\r
640                         (scsiDev.msgOut & 0x7) // We only support LUN 0!\r
641                         )\r
642                 {\r
643                         messageReject();\r
644                 }\r
645         }\r
646         else if (scsiDev.msgOut >= 0x20 && scsiDev.msgOut <= 0x2F)\r
647         {\r
648                 // Two byte message. We don't support these. read and discard.\r
649                 scsiReadByte();\r
650         }\r
651         else if (scsiDev.msgOut == 0x01)\r
652         {\r
653                 int i;\r
654 \r
655                 // Extended message.\r
656                 int msgLen = scsiReadByte();\r
657                 if (msgLen == 0) msgLen = 256;\r
658                 for (i = 0; i < msgLen && !scsiDev.resetFlag; ++i)\r
659                 {\r
660                         // Discard bytes.\r
661                         scsiReadByte();\r
662                 }\r
663 \r
664                 // We don't support ANY extended messages.\r
665                 // Modify Data Pointer:  We don't support reselection.\r
666                 // Wide Data Transfer Request: No. 8bit only.\r
667                 // Synchronous data transfer request. No, we can't do that.\r
668                 // We don't support any 2-byte messages either.\r
669                 // And we don't support any optional 1-byte messages.\r
670                 // In each case, the correct response is MESSAGE REJECT.\r
671                 messageReject();\r
672         }\r
673         else\r
674         {\r
675                 messageReject();\r
676         }\r
677 \r
678         // Re-check the ATN flag in case it stays asserted.\r
679         scsiDev.atnFlag |= SCSI_ReadPin(SCSI_ATN_INT);\r
680 }\r
681 \r
682 void scsiPoll(void)\r
683 {\r
684         if (scsiDev.resetFlag)\r
685         {\r
686                 scsiReset();\r
687                 if ((scsiDev.resetFlag = SCSI_ReadPin(SCSI_RST_INT)))\r
688                 {\r
689                         // Still in reset phase. Do not try and process any commands.\r
690                         return;\r
691                 }\r
692         }\r
693 \r
694         switch (scsiDev.phase)\r
695         {\r
696         case BUS_FREE:\r
697                 if (SCSI_ReadPin(SCSI_In_BSY))\r
698                 {\r
699                         scsiDev.phase = BUS_BUSY;\r
700                 }\r
701                 // The Arbitration phase is optional for SCSI1/SASI hosts if there is only\r
702                 // one initiator in the chain. Support this by moving\r
703                 // straight to selection if SEL is asserted.\r
704                 // ie. the initiator won't assert BSY and it's own ID before moving to selection.\r
705                 else if (SCSI_ReadPin(SCSI_In_SEL))\r
706                 {\r
707                         enter_SelectionPhase();\r
708                 }\r
709         break;\r
710 \r
711         case BUS_BUSY:\r
712                 // Someone is using the bus. Perhaps they are trying to\r
713                 // select us.\r
714                 if (SCSI_ReadPin(SCSI_In_SEL))\r
715                 {\r
716                         enter_SelectionPhase();\r
717                 }\r
718                 else if (!SCSI_ReadPin(SCSI_In_BSY))\r
719                 {\r
720                         scsiDev.phase = BUS_FREE;\r
721                 }\r
722         break;\r
723 \r
724         case ARBITRATION:\r
725                 // TODO Support reselection.\r
726                 break;\r
727 \r
728         case SELECTION:\r
729                 process_SelectionPhase();\r
730         break;\r
731 \r
732         case RESELECTION:\r
733                 // Not currently supported!\r
734         break;\r
735 \r
736         case COMMAND:\r
737                 // Do not check ATN here. SCSI 1 & 2 initiators must set ATN\r
738                 // and SEL together upon entering the selection phase if they\r
739                 // want to send a message (IDENTIFY) immediately.\r
740                 if (scsiDev.atnFlag)\r
741                 {\r
742                         process_MessageOut();\r
743                 }\r
744                 else\r
745                 {\r
746                         process_Command();\r
747                 }\r
748         break;\r
749 \r
750         case DATA_IN:\r
751                 scsiDev.atnFlag |= SCSI_ReadPin(SCSI_ATN_INT);\r
752                 if (scsiDev.atnFlag)\r
753                 {\r
754                         process_MessageOut();\r
755                 }\r
756                 else\r
757                 {\r
758                         process_DataIn();\r
759                 }\r
760         break;\r
761 \r
762         case DATA_OUT:\r
763                 scsiDev.atnFlag |= SCSI_ReadPin(SCSI_ATN_INT);\r
764                 if (scsiDev.atnFlag)\r
765                 {\r
766                         process_MessageOut();\r
767                 }\r
768                 else\r
769                 {\r
770                         process_DataOut();\r
771                 }\r
772         break;\r
773 \r
774         case STATUS:\r
775                 scsiDev.atnFlag |= SCSI_ReadPin(SCSI_ATN_INT);\r
776                 if (scsiDev.atnFlag)\r
777                 {\r
778                         process_MessageOut();\r
779                 }\r
780                 else\r
781                 {\r
782                         process_Status();\r
783                 }\r
784         break;\r
785 \r
786         case MESSAGE_IN:\r
787                 scsiDev.atnFlag |= SCSI_ReadPin(SCSI_ATN_INT);\r
788                 if (scsiDev.atnFlag)\r
789                 {\r
790                         process_MessageOut();\r
791                 }\r
792                 else\r
793                 {\r
794                         process_MessageIn();\r
795                 }\r
796 \r
797         break;\r
798 \r
799         case MESSAGE_OUT:\r
800                 process_MessageOut();\r
801         break;\r
802         }\r
803 }\r
804 \r
805 void scsiInit()\r
806 {\r
807         scsiDev.scsiIdMask = 1 << (config->scsiId);\r
808 \r
809         scsiDev.atnFlag = 0;\r
810         scsiDev.resetFlag = 1;\r
811         scsiDev.phase = BUS_FREE;\r
812         scsiDev.reservedId = -1;\r
813         scsiDev.reserverId = -1;\r
814         scsiDev.unitAttention = POWER_ON_RESET;\r
815 }\r
816 \r