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