af9d1f74a94926ef5c42ee5d4bbad840fe616edf
[SCSI2SD-V6.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 \r
429                 // Save our initiator now that we're no longer in a time-critical\r
430                 // section.\r
431                 // SCSI1/SASI initiators may not set their own ID.\r
432                 if (maskBitCount == 2)\r
433                 {\r
434                         int i;\r
435                         uint8 initiatorMask = mask ^ scsiDev.scsiIdMask;\r
436                         scsiDev.initiatorId = 0;\r
437                         for (i = 0; i < 8; ++i)\r
438                         {\r
439                                 if (initiatorMask & (1 << i))\r
440                                 {\r
441                                         scsiDev.initiatorId = i;\r
442                                         break;\r
443                                 }\r
444                         }\r
445                 }\r
446                 else\r
447                 {\r
448                         scsiDev.initiatorId = -1;\r
449                 }\r
450 \r
451                 scsiDev.phase = COMMAND;\r
452         }\r
453         else if (!sel)\r
454         {\r
455                 scsiDev.phase = BUS_BUSY;\r
456         }\r
457 }\r
458 \r
459 static void process_MessageOut()\r
460 {\r
461         scsiEnterPhase(MESSAGE_OUT);\r
462 \r
463         scsiDev.atnFlag = 0;\r
464         scsiDev.parityError = 0;\r
465         scsiDev.msgOut = scsiReadByte();\r
466 #ifdef MM_DEBUG\r
467         scsiDev.msgCount++;\r
468 #endif\r
469 \r
470         if (scsiDev.parityError)\r
471         {\r
472                 // Skip the remaining message bytes, and then start the MESSAGE_OUT\r
473                 // phase again from the start. The initiator will re-send the\r
474                 // same set of messages.\r
475                 while (SCSI_ReadPin(SCSI_ATN_INT) && !scsiDev.resetFlag)\r
476                 {\r
477                         scsiReadByte();\r
478                 }\r
479 \r
480                 // Go-back and try the message again.\r
481                 scsiDev.atnFlag = 1;\r
482                 scsiDev.parityError = 0;\r
483         }\r
484         else if (scsiDev.msgOut == 0x00)\r
485         {\r
486                 // COMMAND COMPLETE. but why would the target be receiving this ? nfi.\r
487                 enter_BusFree();\r
488         }\r
489         else if (scsiDev.msgOut == 0x06)\r
490         {\r
491                 // ABORT\r
492                 scsiDiskReset();\r
493                 enter_BusFree();\r
494         }\r
495         else if (scsiDev.msgOut == 0x0C)\r
496         {\r
497                 // BUS DEVICE RESET\r
498 \r
499                 scsiDiskReset();\r
500 \r
501                 scsiDev.unitAttention = SCSI_BUS_RESET;\r
502 \r
503                 // ANY initiator can reset the reservation state via this message.\r
504                 scsiDev.reservedId = -1;\r
505                 scsiDev.reserverId = -1;\r
506                 enter_BusFree();\r
507         }\r
508         else if (scsiDev.msgOut == 0x05)\r
509         {\r
510                 // Initiate Detected Error\r
511                 // Ignore for now\r
512         }\r
513         else if (scsiDev.msgOut == 0x0F)\r
514         {\r
515                 // INITIATE RECOVERY\r
516                 // Ignore for now\r
517         }\r
518         else if (scsiDev.msgOut == 0x10)\r
519         {\r
520                 // RELEASE RECOVERY\r
521                 // Ignore for now\r
522                 enter_BusFree();\r
523         }\r
524         else if (scsiDev.msgOut == MSG_REJECT)\r
525         {\r
526                 // Message Reject\r
527                 // Oh well.\r
528                 scsiDev.resetFlag = 1;\r
529         }\r
530         else if (scsiDev.msgOut == 0x08)\r
531         {\r
532                 // NOP\r
533         }\r
534         else if (scsiDev.msgOut == 0x09)\r
535         {\r
536                 // Message Parity Error\r
537                 // Go back and re-send the last message.\r
538                 scsiDev.phase = MESSAGE_IN;\r
539         }\r
540         else if (scsiDev.msgOut & 0x80) // 0x80 -> 0xFF\r
541         {\r
542                 // IDENTIFY\r
543                 // We don't disconnect, so ignore disconnect privilege.\r
544                 if ((scsiDev.msgOut & 0x18) || // Reserved bits set.\r
545                         (scsiDev.msgOut & 0x20)  || // We don't have any target routines!\r
546                         (scsiDev.msgOut & 0x7) // We only support LUN 0!\r
547                         )\r
548                 {\r
549                         messageReject();\r
550                 }\r
551         }\r
552         else if (scsiDev.msgOut >= 0x20 && scsiDev.msgOut <= 0x2F)\r
553         {\r
554                 // Two byte message. We don't support these. read and discard.\r
555                 scsiReadByte();\r
556         }\r
557         else if (scsiDev.msgOut == 0x01)\r
558         {\r
559                 int i;\r
560 \r
561                 // Extended message.\r
562                 int msgLen = scsiReadByte();\r
563                 if (msgLen == 0) msgLen = 256;\r
564                 for (i = 0; i < msgLen && !scsiDev.resetFlag; ++i)\r
565                 {\r
566                         // Discard bytes.\r
567                         scsiReadByte();\r
568                 }\r
569 \r
570                 // We don't support ANY extended messages.\r
571                 // Modify Data Pointer:  We don't support reselection.\r
572                 // Wide Data Transfer Request: No. 8bit only.\r
573                 // Synchronous data transfer request. No, we can't do that.\r
574                 // We don't support any 2-byte messages either.\r
575                 // And we don't support any optional 1-byte messages.\r
576                 // In each case, the correct response is MESSAGE REJECT.\r
577                 messageReject();\r
578         }\r
579         else\r
580         {\r
581                 messageReject();\r
582         }\r
583 \r
584         // Re-check the ATN flag in case it stays asserted.\r
585         scsiDev.atnFlag |= SCSI_ReadPin(SCSI_ATN_INT);\r
586 }\r
587 \r
588 void scsiPoll(void)\r
589 {\r
590         if (scsiDev.resetFlag)\r
591         {\r
592                 scsiReset();\r
593         }\r
594 \r
595         switch (scsiDev.phase)\r
596         {\r
597         case BUS_FREE:\r
598                 if (SCSI_ReadPin(SCSI_In_BSY))\r
599                 {\r
600                         scsiDev.phase = BUS_BUSY;\r
601                 }\r
602                 // The Arbitration phase is optional for SCSI1/SASI hosts if there is only\r
603                 // one initiator in the chain. Support this by moving\r
604                 // straight to selection if SEL is asserted.\r
605                 // ie. the initiator won't assert BSY and it's own ID before moving to selection.\r
606                 else if (SCSI_ReadPin(SCSI_In_SEL))\r
607                 {\r
608                         enter_SelectionPhase();\r
609                 }\r
610         break;\r
611 \r
612         case BUS_BUSY:\r
613                 // Someone is using the bus. Perhaps they are trying to\r
614                 // select us.\r
615                 if (SCSI_ReadPin(SCSI_In_SEL))\r
616                 {\r
617                         enter_SelectionPhase();\r
618                 }\r
619                 else if (!SCSI_ReadPin(SCSI_In_BSY))\r
620                 {\r
621                         scsiDev.phase = BUS_FREE;\r
622                 }\r
623         break;\r
624 \r
625         case ARBITRATION:\r
626                 // TODO Support reselection.\r
627                 break;\r
628 \r
629         case SELECTION:\r
630                 process_SelectionPhase();\r
631         break;\r
632 \r
633         case RESELECTION:\r
634                 // Not currently supported!\r
635         break;\r
636 \r
637         case COMMAND:\r
638                 // Do not check ATN here. SCSI 1 & 2 initiators must set ATN\r
639                 // and SEL together upon entering the selection phase if they\r
640                 // want to send a message (IDENTIFY) immediately.\r
641                 if (scsiDev.atnFlag)\r
642                 {\r
643                         process_MessageOut();\r
644                 }\r
645                 else\r
646                 {\r
647                         process_Command();\r
648                 }\r
649         break;\r
650 \r
651         case DATA_IN:\r
652                 scsiDev.atnFlag |= SCSI_ReadPin(SCSI_ATN_INT);\r
653                 if (scsiDev.atnFlag)\r
654                 {\r
655                         process_MessageOut();\r
656                 }\r
657                 else\r
658                 {\r
659                         process_DataIn();\r
660                 }\r
661         break;\r
662 \r
663         case DATA_OUT:\r
664                 scsiDev.atnFlag |= SCSI_ReadPin(SCSI_ATN_INT);\r
665                 if (scsiDev.atnFlag)\r
666                 {\r
667                         process_MessageOut();\r
668                 }\r
669                 else\r
670                 {\r
671                         process_DataOut();\r
672                 }\r
673         break;\r
674 \r
675         case STATUS:\r
676                 scsiDev.atnFlag |= SCSI_ReadPin(SCSI_ATN_INT);\r
677                 if (scsiDev.atnFlag)\r
678                 {\r
679                         process_MessageOut();\r
680                 }\r
681                 else\r
682                 {\r
683                         process_Status();\r
684                 }\r
685         break;\r
686 \r
687         case MESSAGE_IN:\r
688                 scsiDev.atnFlag |= SCSI_ReadPin(SCSI_ATN_INT);\r
689                 if (scsiDev.atnFlag)\r
690                 {\r
691                         process_MessageOut();\r
692                 }\r
693                 else\r
694                 {\r
695                         process_MessageIn();\r
696                 }\r
697 \r
698         break;\r
699 \r
700         case MESSAGE_OUT:\r
701                 process_MessageOut();\r
702         break;\r
703         }\r
704 }\r
705 \r
706 void scsiInit()\r
707 {\r
708         scsiDev.scsiIdMask = 1 << (config->scsiId);\r
709 \r
710         scsiDev.atnFlag = 0;\r
711         scsiDev.resetFlag = 1;\r
712         scsiDev.phase = BUS_FREE;\r
713         scsiDev.reservedId = -1;\r
714         scsiDev.reserverId = -1;\r
715         scsiDev.unitAttention = POWER_ON_RESET;\r
716 }\r
717 \r