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