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