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