016ecbb523f05d58bbc768c1442c425cf9290354
[SCSI2SD-V6.git] / src / firmware / 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 "scsi.h"\r
19 #include "scsiPhy.h"\r
20 #include "config.h"\r
21 #include "diagnostic.h"\r
22 #include "disk.h"\r
23 #include "inquiry.h"\r
24 #include "led.h"\r
25 #include "mode.h"\r
26 #include "time.h"\r
27 #include "bsp.h"\r
28 #include "cdrom.h"\r
29 //#include "debug.h"\r
30 #include "tape.h"\r
31 #include "mo.h"\r
32 #include "vendor.h"\r
33 \r
34 #include <string.h>\r
35 \r
36 // Global SCSI device state.\r
37 ScsiDevice scsiDev S2S_DMA_ALIGN;\r
38 \r
39 static void enter_SelectionPhase(void);\r
40 static void process_SelectionPhase(void);\r
41 static void enter_MessageIn(uint8_t message);\r
42 static void enter_Status(uint8_t status);\r
43 static void enter_DataIn(int len);\r
44 static void process_DataIn(void);\r
45 static void process_DataOut(void);\r
46 static void process_Command(void);\r
47 \r
48 static void doReserveRelease(void);\r
49 \r
50 void enter_BusFree()\r
51 {\r
52         // This delay probably isn't needed for most SCSI hosts, but it won't\r
53         // hurt either. It's possible some of the samplers needed this delay.\r
54         if (scsiDev.compatMode < COMPAT_SCSI2)\r
55         {\r
56                 s2s_delay_us(2);\r
57         }\r
58 \r
59 #if 0\r
60         if (scsiDev.status != GOOD && isDebugEnabled())\r
61         {\r
62                 // We want to capture debug information for failure cases.\r
63                 s2s_delay_ms(64);\r
64         }\r
65 #endif\r
66 \r
67 \r
68         scsiEnterBusFree();\r
69 \r
70         // Wait for the initiator to cease driving signals\r
71         // Bus settle delay + bus clear delay = 1200ns\r
72         s2s_delay_us(2);\r
73 \r
74 \r
75         s2s_ledOff();\r
76         scsiDev.phase = BUS_FREE;\r
77         scsiDev.selFlag = 0;\r
78 }\r
79 \r
80 static void enter_MessageIn(uint8_t message)\r
81 {\r
82         scsiDev.msgIn = message;\r
83         scsiDev.phase = MESSAGE_IN;\r
84 }\r
85 \r
86 int process_MessageIn(int releaseBusFree)\r
87 {\r
88         scsiEnterPhase(MESSAGE_IN);\r
89         scsiWriteByte(scsiDev.msgIn);\r
90 \r
91         if (unlikely(scsiDev.atnFlag))\r
92         {\r
93                 // If there was a parity error, we go\r
94                 // back to MESSAGE_OUT first, get out parity error message, then come\r
95                 // back here.\r
96                 return 0;\r
97         }\r
98         else if ((scsiDev.msgIn == MSG_LINKED_COMMAND_COMPLETE) ||\r
99                 (scsiDev.msgIn == MSG_LINKED_COMMAND_COMPLETE_WITH_FLAG))\r
100         {\r
101                 // Go back to the command phase and start again.\r
102                 scsiDev.phase = COMMAND;\r
103                 scsiDev.dataPtr = 0;\r
104                 scsiDev.savedDataPtr = 0;\r
105                 scsiDev.dataLen = 0;\r
106                 scsiDev.status = GOOD;\r
107                 transfer.blocks = 0;\r
108                 transfer.currentBlock = 0;\r
109                 return 0;\r
110         }\r
111         else if (releaseBusFree) /*if (scsiDev.msgIn == MSG_COMMAND_COMPLETE)*/\r
112         {\r
113                 enter_BusFree();\r
114                 return 1;\r
115         }\r
116         else\r
117         {\r
118                 return 1;\r
119         }\r
120 }\r
121 \r
122 static void messageReject()\r
123 {\r
124         scsiEnterPhase(MESSAGE_IN);\r
125         scsiWriteByte(MSG_REJECT);\r
126 }\r
127 \r
128 static void enter_Status(uint8_t status)\r
129 {\r
130         scsiDev.status = status;\r
131         scsiDev.phase = STATUS;\r
132 \r
133         scsiDev.lastStatus = scsiDev.status;\r
134         scsiDev.lastSense = scsiDev.target->sense.code;\r
135         scsiDev.lastSenseASC = scsiDev.target->sense.asc;\r
136 }\r
137 \r
138 void process_Status()\r
139 {\r
140         scsiEnterPhase(STATUS);\r
141 \r
142         uint8_t message;\r
143 \r
144         uint8_t control = scsiDev.cdb[scsiDev.cdbLen - 1];\r
145 \r
146         if (scsiDev.target->cfg->quirks == S2S_CFG_QUIRKS_OMTI)\r
147         {\r
148                 // OMTI non-standard LINK control\r
149                 if (control & 0x01)\r
150                 {\r
151                         scsiDev.phase = COMMAND;\r
152                         return;\r
153                 }\r
154         }\r
155 \r
156         if ((scsiDev.status == GOOD) && (control & 0x01) &&\r
157                 scsiDev.target->cfg->quirks != S2S_CFG_QUIRKS_XEBEC)\r
158         {\r
159                 // Linked command.\r
160                 scsiDev.status = INTERMEDIATE;\r
161                 if (control & 0x02)\r
162                 {\r
163                         message = MSG_LINKED_COMMAND_COMPLETE_WITH_FLAG;\r
164                 }\r
165                 else\r
166                 {\r
167                         message = MSG_LINKED_COMMAND_COMPLETE;\r
168                 }\r
169         }\r
170         else\r
171         {\r
172                 message = MSG_COMMAND_COMPLETE;\r
173         }\r
174 \r
175         if (scsiDev.target->cfg->quirks == S2S_CFG_QUIRKS_XEBEC)\r
176         {\r
177                 // More non-standardness. Expects 2 status bytes (really status + msg)\r
178                 // 00 d 000 err 0\r
179                 // d == disk number\r
180                 // ERR = 1 if error.\r
181                 if (scsiDev.status == GOOD)\r
182                 {\r
183                         scsiWriteByte(scsiDev.cdb[1] & 0x20);\r
184                 }\r
185                 else\r
186                 {\r
187                         scsiWriteByte((scsiDev.cdb[1] & 0x20) | 0x2);\r
188                 }\r
189                 s2s_delay_us(10); // Seems to need a delay before changing phase bits.\r
190         }\r
191         else if (scsiDev.target->cfg->quirks == S2S_CFG_QUIRKS_OMTI)\r
192         {\r
193                 scsiDev.status |= (scsiDev.target->targetId & 0x03) << 5;\r
194                 scsiWriteByte(scsiDev.status);\r
195         }\r
196         else\r
197         {\r
198                 scsiWriteByte(scsiDev.status);\r
199         }\r
200 \r
201         scsiDev.lastStatus = scsiDev.status;\r
202         scsiDev.lastSense = scsiDev.target->sense.code;\r
203         scsiDev.lastSenseASC = scsiDev.target->sense.asc;\r
204 \r
205         // Command Complete occurs AFTER a valid status has been\r
206         // sent. then we go bus-free.\r
207         enter_MessageIn(message);\r
208 }\r
209 \r
210 static void enter_DataIn(int len)\r
211 {\r
212         scsiDev.dataLen = len;\r
213         scsiDev.phase = DATA_IN;\r
214 }\r
215 \r
216 static void process_DataIn()\r
217 {\r
218         uint32_t len;\r
219 \r
220         if (scsiDev.dataLen > sizeof(scsiDev.data))\r
221         {\r
222                 scsiDev.dataLen = sizeof(scsiDev.data);\r
223         }\r
224 \r
225         len = scsiDev.dataLen - scsiDev.dataPtr;\r
226         if (len > 0)\r
227         {\r
228                 scsiEnterPhase(DATA_IN);\r
229                 scsiWrite(scsiDev.data + scsiDev.dataPtr, len);\r
230                 scsiDev.dataPtr += len;\r
231         }\r
232 \r
233         if ((scsiDev.dataPtr >= scsiDev.dataLen) &&\r
234                 (transfer.currentBlock == transfer.blocks))\r
235         {\r
236                 enter_Status(GOOD);\r
237         }\r
238 }\r
239 \r
240 static void process_DataOut()\r
241 {\r
242         uint32_t len;\r
243 \r
244         if (scsiDev.dataLen > sizeof(scsiDev.data))\r
245         {\r
246                 scsiDev.dataLen = sizeof(scsiDev.data);\r
247         }\r
248 \r
249         len = scsiDev.dataLen - scsiDev.dataPtr;\r
250         if (len > 0)\r
251         {\r
252                 scsiEnterPhase(DATA_OUT);\r
253 \r
254                 int parityError = 0;\r
255                 scsiRead(scsiDev.data + scsiDev.dataPtr, len, &parityError);\r
256                 scsiDev.dataPtr += len;\r
257 \r
258                 if (parityError &&\r
259                         (scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY))\r
260                 {\r
261                         scsiDev.target->sense.code = ABORTED_COMMAND;\r
262                         scsiDev.target->sense.asc = SCSI_PARITY_ERROR;\r
263                         enter_Status(CHECK_CONDITION);\r
264                 }\r
265         }\r
266 \r
267         if ((scsiDev.dataPtr >= scsiDev.dataLen) &&\r
268                 (transfer.currentBlock == transfer.blocks))\r
269         {\r
270                 if (scsiDev.postDataOutHook != NULL)\r
271                 {\r
272                         scsiDev.postDataOutHook();\r
273                 }\r
274                 else\r
275                 {\r
276                         enter_Status(GOOD);\r
277                 }\r
278         }\r
279 }\r
280 \r
281 static const uint8_t CmdGroupBytes[8] = {6, 10, 10, 6, 6, 12, 6, 6};\r
282 static void process_Command()\r
283 {\r
284         int group;\r
285         uint8_t command;\r
286         uint8_t control;\r
287 \r
288         scsiEnterPhase(COMMAND);\r
289 \r
290         memset(scsiDev.cdb + 6, 0, sizeof(scsiDev.cdb) - 6);\r
291         int parityError = 0;\r
292         scsiRead(scsiDev.cdb, 6, &parityError);\r
293 \r
294         group = scsiDev.cdb[0] >> 5;\r
295         scsiDev.cdbLen = CmdGroupBytes[group];\r
296         if (parityError &&\r
297                 (scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY))\r
298         {\r
299                 // Don't try and read more bytes, as we cannot be sure what group\r
300                 // the command should be.\r
301         }\r
302         else if (scsiDev.cdbLen - 6 > 0)\r
303         {\r
304                 scsiRead(scsiDev.cdb + 6, scsiDev.cdbLen - 6, &parityError);\r
305         }\r
306         command = scsiDev.cdb[0];\r
307 \r
308         // Prefer LUN's set by IDENTIFY messages for newer hosts.\r
309         if (scsiDev.lun < 0)\r
310         {\r
311                 if (command == 0xE0 || command == 0xE4) // XEBEC s1410\r
312                 {\r
313                         scsiDev.lun = 0;\r
314                 }\r
315                 else\r
316                 {\r
317                         scsiDev.lun = scsiDev.cdb[1] >> 5;\r
318                 }\r
319         }\r
320 \r
321 \r
322         // For Philips P2000C with Xebec S1410 SASI/MFM adapter\r
323         // http://bitsavers.trailing-edge.com/pdf/xebec/104524C_S1410Man_Aug83.pdf\r
324         if ((scsiDev.lun > 0) && (scsiDev.boardCfg.flags & S2S_CFG_MAP_LUNS_TO_IDS))\r
325         {\r
326                 int tgtIndex;\r
327                 for (tgtIndex = 0; tgtIndex < S2S_MAX_TARGETS; ++tgtIndex)\r
328                 {\r
329                         if (scsiDev.targets[tgtIndex].targetId == scsiDev.lun)\r
330                         {\r
331                                 scsiDev.target = &scsiDev.targets[tgtIndex];\r
332                                 scsiDev.lun = 0;\r
333                                 break;\r
334                         }\r
335                 }\r
336         }\r
337 \r
338         control = scsiDev.cdb[scsiDev.cdbLen - 1];\r
339 \r
340         scsiDev.cmdCount++;\r
341         const S2S_TargetCfg* cfg = scsiDev.target->cfg;\r
342 \r
343         if (unlikely(scsiDev.resetFlag))\r
344         {\r
345                 // Don't log bogus commands\r
346                 scsiDev.cmdCount--;\r
347                 memset(scsiDev.cdb, 0xff, sizeof(scsiDev.cdb));\r
348                 return;\r
349         }\r
350         else if (parityError &&\r
351                 (scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY))\r
352         {\r
353                 scsiDev.target->sense.code = ABORTED_COMMAND;\r
354                 scsiDev.target->sense.asc = SCSI_PARITY_ERROR;\r
355                 enter_Status(CHECK_CONDITION);\r
356         }\r
357         else if ((control & 0x02) && ((control & 0x01) == 0) &&\r
358                 // used for head step options on xebec.\r
359                 likely(scsiDev.target->cfg->quirks != S2S_CFG_QUIRKS_XEBEC))\r
360         {\r
361                 // FLAG set without LINK flag.\r
362                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
363                 scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
364                 enter_Status(CHECK_CONDITION);\r
365         }\r
366         else if (command == 0x12)\r
367         {\r
368                 s2s_scsiInquiry();\r
369         }\r
370         else if (command == 0x03)\r
371         {\r
372                 // REQUEST SENSE\r
373                 uint32_t allocLength = scsiDev.cdb[4];\r
374 \r
375                 if (scsiDev.target->cfg->quirks == S2S_CFG_QUIRKS_XEBEC)\r
376                 {\r
377                         // Completely non-standard\r
378                         allocLength = 4;\r
379                         if (scsiDev.target->sense.code == NO_SENSE)\r
380                                 scsiDev.data[0] = 0;\r
381                         else if (scsiDev.target->sense.code == ILLEGAL_REQUEST)\r
382                                 scsiDev.data[0] = 0x20; // Illegal command\r
383                         else if (scsiDev.target->sense.code == NOT_READY)\r
384                                 scsiDev.data[0] = 0x04; // Drive not ready\r
385                         else\r
386                                 scsiDev.data[0] = 0x11;  // Uncorrectable data error\r
387 \r
388                         scsiDev.data[1] = (scsiDev.cdb[1] & 0x20) | ((transfer.lba >> 16) & 0x1F);\r
389                         scsiDev.data[2] = transfer.lba >> 8;\r
390                         scsiDev.data[3] = transfer.lba;\r
391                 }\r
392                 else\r
393                 {\r
394                         // As specified by the SASI and SCSI1 standard.\r
395                         // Newer initiators won't be specifying 0 anyway.\r
396                         if (allocLength == 0) allocLength = 4;\r
397 \r
398                         memset(scsiDev.data, 0, 256); // Max possible alloc length\r
399                         scsiDev.data[0] = 0xF0;\r
400                         scsiDev.data[2] = scsiDev.target->sense.code & 0x0F;\r
401 \r
402                         scsiDev.data[3] = transfer.lba >> 24;\r
403                         scsiDev.data[4] = transfer.lba >> 16;\r
404                         scsiDev.data[5] = transfer.lba >> 8;\r
405                         scsiDev.data[6] = transfer.lba;\r
406 \r
407                         // Additional bytes if there are errors to report\r
408                         scsiDev.data[7] = 10; // additional length\r
409                         scsiDev.data[12] = scsiDev.target->sense.asc >> 8;\r
410                         scsiDev.data[13] = scsiDev.target->sense.asc;\r
411                 }\r
412 \r
413                 // Silently truncate results. SCSI-2 spec 8.2.14.\r
414                 enter_DataIn(allocLength);\r
415 \r
416                 // This is a good time to clear out old sense information.\r
417                 scsiDev.target->sense.code = NO_SENSE;\r
418                 scsiDev.target->sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;\r
419         }\r
420         // Some old SCSI drivers do NOT properly support\r
421         // unitAttention. eg. the Mac Plus would trigger a SCSI reset\r
422         // on receiving the unit attention response on boot, thus\r
423         // triggering another unit attention condition.\r
424         else if (scsiDev.target->unitAttention &&\r
425                 (scsiDev.boardCfg.flags & S2S_CFG_ENABLE_UNIT_ATTENTION))\r
426         {\r
427                 scsiDev.target->sense.code = UNIT_ATTENTION;\r
428                 scsiDev.target->sense.asc = scsiDev.target->unitAttention;\r
429 \r
430                 // If initiator doesn't do REQUEST SENSE for the next command, then\r
431                 // data is lost.\r
432                 scsiDev.target->unitAttention = 0;\r
433 \r
434                 enter_Status(CHECK_CONDITION);\r
435         }\r
436         else if (scsiDev.lun)\r
437         {\r
438                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
439                 scsiDev.target->sense.asc = LOGICAL_UNIT_NOT_SUPPORTED;\r
440                 enter_Status(CHECK_CONDITION);\r
441         }\r
442         else if (command == 0x17 || command == 0x16)\r
443         {\r
444                 doReserveRelease();\r
445         }\r
446         else if ((scsiDev.target->reservedId >= 0) &&\r
447                 (scsiDev.target->reservedId != scsiDev.initiatorId))\r
448         {\r
449                 enter_Status(CONFLICT);\r
450         }\r
451         // Handle odd device types first that may override basic read and\r
452         // write commands. Will fall-through to generic disk handling.\r
453         else if (((cfg->deviceType == S2S_CFG_OPTICAL) && scsiCDRomCommand()) ||\r
454                 ((cfg->deviceType == S2S_CFG_SEQUENTIAL) && scsiTapeCommand()) ||\r
455                 ((cfg->deviceType == S2S_CFG_MO) && scsiMOCommand()))\r
456         {\r
457                 // Already handled.\r
458         }\r
459         else if (scsiDiskCommand())\r
460         {\r
461                 // Already handled.\r
462                 // check for the performance-critical read/write\r
463                 // commands ASAP.\r
464         }\r
465         else if (command == 0x1C)\r
466         {\r
467                 scsiReceiveDiagnostic();\r
468         }\r
469         else if (command == 0x1D)\r
470         {\r
471                 scsiSendDiagnostic();\r
472         }\r
473         else if (command == 0x3B)\r
474         {\r
475                 scsiWriteBuffer();\r
476         }\r
477         else if (command == 0x0f &&\r
478                 scsiDev.target->cfg->quirks == S2S_CFG_QUIRKS_XEBEC)\r
479         {\r
480                 scsiWriteSectorBuffer();\r
481         }\r
482         else if (command == 0x3C)\r
483         {\r
484                 scsiReadBuffer();\r
485         }\r
486         else if (!scsiModeCommand() && !scsiVendorCommand())\r
487         {\r
488                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
489                 scsiDev.target->sense.asc = INVALID_COMMAND_OPERATION_CODE;\r
490                 enter_Status(CHECK_CONDITION);\r
491         }\r
492 \r
493         // Successful\r
494         if (scsiDev.phase == COMMAND) // No status set, and not in DATA_IN\r
495         {\r
496                 enter_Status(GOOD);\r
497         }\r
498 \r
499 }\r
500 \r
501 static void doReserveRelease()\r
502 {\r
503         int extentReservation = scsiDev.cdb[1] & 1;\r
504         int thirdPty = scsiDev.cdb[1] & 0x10;\r
505         int thirdPtyId = (scsiDev.cdb[1] >> 1) & 0x7;\r
506         uint8_t command = scsiDev.cdb[0];\r
507 \r
508         int canRelease =\r
509                 (!thirdPty && (scsiDev.initiatorId == scsiDev.target->reservedId)) ||\r
510                         (thirdPty &&\r
511                                 (scsiDev.target->reserverId == scsiDev.initiatorId) &&\r
512                                 (scsiDev.target->reservedId == thirdPtyId)\r
513                         );\r
514 \r
515         if (extentReservation)\r
516         {\r
517                 // Not supported.\r
518                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
519                 scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
520                 enter_Status(CHECK_CONDITION);\r
521         }\r
522         else if (command == 0x17) // release\r
523         {\r
524                 if ((scsiDev.target->reservedId < 0) || canRelease)\r
525                 {\r
526                         scsiDev.target->reservedId = -1;\r
527                         scsiDev.target->reserverId = -1;\r
528                 }\r
529                 else\r
530                 {\r
531                         enter_Status(CONFLICT);\r
532                 }\r
533         }\r
534         else // assume reserve.\r
535         {\r
536                 if ((scsiDev.target->reservedId < 0) || canRelease)\r
537                 {\r
538                         scsiDev.target->reserverId = scsiDev.initiatorId;\r
539                         if (thirdPty)\r
540                         {\r
541                                 scsiDev.target->reservedId = thirdPtyId;\r
542                         }\r
543                         else\r
544                         {\r
545                                 scsiDev.target->reservedId = scsiDev.initiatorId;\r
546                         }\r
547                 }\r
548                 else\r
549                 {\r
550                         // Already reserved by someone else!\r
551                         enter_Status(CONFLICT);\r
552                 }\r
553         }\r
554 }\r
555 \r
556 static uint32_t resetUntil = 0;\r
557 \r
558 static void scsiReset()\r
559 {\r
560         scsiDev.rstCount++;\r
561         s2s_ledOff();\r
562 \r
563         scsiPhyReset();\r
564 \r
565         scsiDev.phase = BUS_FREE;\r
566         scsiDev.atnFlag = 0;\r
567         scsiDev.resetFlag = 0;\r
568         scsiDev.selFlag = 0;\r
569         scsiDev.lun = -1;\r
570         scsiDev.compatMode = COMPAT_UNKNOWN;\r
571 \r
572         if (scsiDev.target)\r
573         {\r
574                 if (scsiDev.target->unitAttention != POWER_ON_RESET)\r
575                 {\r
576                         scsiDev.target->unitAttention = SCSI_BUS_RESET;\r
577                 }\r
578                 scsiDev.target->reservedId = -1;\r
579                 scsiDev.target->reserverId = -1;\r
580                 scsiDev.target->sense.code = NO_SENSE;\r
581                 scsiDev.target->sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;\r
582         }\r
583         scsiDev.target = NULL;\r
584 \r
585         for (int i = 0; i < S2S_MAX_TARGETS; ++i)\r
586         {\r
587                 scsiDev.targets[i].syncOffset = 0;\r
588                 scsiDev.targets[i].syncPeriod = 0;\r
589         }\r
590         scsiDev.minSyncPeriod = 0;\r
591 \r
592         scsiDiskReset();\r
593 \r
594         scsiDev.postDataOutHook = NULL;\r
595 \r
596         scsiDev.sdUnderrunCount = 0;\r
597 \r
598         // Sleep to allow the bus to settle down a bit.\r
599         // We must be ready again within the "Reset to selection time" of\r
600         // 250ms.\r
601         // There is no guarantee that the RST line will be negated by then.\r
602         // NOTE: We could be connected and powered by USB for configuration,\r
603         // in which case TERMPWR cannot be supplied, and reset will ALWAYS\r
604         // be true. Therefore, the sleep here must be slow to avoid slowing\r
605         // USB comms\r
606         resetUntil = s2s_getTime_ms() + 2; // At least 1ms.\r
607 }\r
608 \r
609 static void enter_SelectionPhase()\r
610 {\r
611         // Ignore stale versions of this flag, but ensure we know the\r
612         // current value if the flag is still set.\r
613         scsiDev.atnFlag = 0;\r
614         scsiDev.dataPtr = 0;\r
615         scsiDev.savedDataPtr = 0;\r
616         scsiDev.dataLen = 0;\r
617         scsiDev.status = GOOD;\r
618         scsiDev.phase = SELECTION;\r
619         scsiDev.lun = -1;\r
620         scsiDev.discPriv = 0;\r
621 \r
622         scsiDev.initiatorId = -1;\r
623         scsiDev.target = NULL;\r
624 \r
625         transfer.blocks = 0;\r
626         transfer.currentBlock = 0;\r
627 \r
628         scsiDev.postDataOutHook = NULL;\r
629 \r
630         scsiDev.needSyncNegotiationAck = 0;\r
631 }\r
632 \r
633 static void process_SelectionPhase()\r
634 {\r
635         // Selection delays.\r
636         // Many SCSI1 samplers that use a 5380 chip need a delay of at least 1ms.\r
637         // The Mac Plus boot-time (ie. rom code) selection abort time\r
638         // is < 1ms and must have no delay (standard suggests 250ms abort time)\r
639         // Most newer SCSI2 hosts don't care either way.\r
640         if (scsiDev.target->cfg->quirks == S2S_CFG_QUIRKS_XEBEC)\r
641         {\r
642                 s2s_delay_ms(1); // Simply won't work if set to 0.\r
643         }\r
644         else if (scsiDev.boardCfg.selectionDelay == 255) // auto\r
645         {\r
646                 if (scsiDev.compatMode < COMPAT_SCSI2)\r
647                 {\r
648                         s2s_delay_ms(1);\r
649                 }\r
650         }\r
651         else if (scsiDev.boardCfg.selectionDelay != 0)\r
652         {\r
653                 s2s_delay_ms(scsiDev.boardCfg.selectionDelay);\r
654         }\r
655 \r
656         uint8_t selStatus = *SCSI_STS_SELECTED;\r
657         if ((selStatus == 0) && (scsiDev.boardCfg.flags & S2S_CFG_ENABLE_SEL_LATCH))\r
658         {\r
659                 selStatus = scsiDev.selFlag;\r
660         }\r
661 \r
662         int tgtIndex;\r
663         TargetState* target = NULL;\r
664         for (tgtIndex = 0; tgtIndex < S2S_MAX_TARGETS; ++tgtIndex)\r
665         {\r
666                 if (scsiDev.targets[tgtIndex].targetId == (selStatus & 7))\r
667                 {\r
668                         target = &scsiDev.targets[tgtIndex];\r
669                         break;\r
670                 }\r
671         }\r
672         if ((target != NULL) && (selStatus & 0x40))\r
673         {\r
674                 // We've been selected!\r
675                 // Assert BSY - Selection success!\r
676                 // must happen within 200us (Selection abort time) of seeing our\r
677                 // ID + SEL.\r
678                 // (Note: the initiator will be waiting the "Selection time-out delay"\r
679                 // for our BSY response, which is actually a very generous 250ms)\r
680                 *SCSI_CTRL_BSY = 1;\r
681                 s2s_ledOn();\r
682 \r
683                 scsiDev.target = target;\r
684 \r
685                 // Do we enter MESSAGE OUT immediately ? SCSI 1 and 2 standards says\r
686                 // move to MESSAGE OUT if ATN is true before we assert BSY.\r
687                 // The initiator should assert ATN with SEL.\r
688                 scsiDev.atnFlag = selStatus & 0x80;\r
689 \r
690 \r
691                 // Unit attention breaks many older SCSI hosts. Disable it completely\r
692                 // for SCSI-1 (and older) hosts, regardless of our configured setting.\r
693                 // Enable the compatability mode also as many SASI and SCSI1\r
694                 // controllers don't generate parity bits.\r
695                 if (!scsiDev.atnFlag)\r
696                 {\r
697                         target->unitAttention = 0;\r
698                         scsiDev.compatMode = COMPAT_SCSI1;\r
699                 }\r
700                 else if (!(scsiDev.boardCfg.flags & S2S_CFG_ENABLE_SCSI2))\r
701                 {\r
702                         scsiDev.compatMode = COMPAT_SCSI2_DISABLED;\r
703                 }\r
704                 else\r
705                 {\r
706                         scsiDev.compatMode = COMPAT_SCSI2;\r
707                 }\r
708 \r
709                 scsiDev.selCount++;\r
710 \r
711 \r
712                 // Save our initiator now that we're no longer in a time-critical\r
713                 // section.\r
714                 // SCSI1/SASI initiators may not set their own ID.\r
715                 scsiDev.initiatorId = (selStatus >> 3) & 0x7;\r
716 \r
717                 // Wait until the end of the selection phase.\r
718                 uint32_t selTimerBegin = s2s_getTime_ms();\r
719                 while (likely(!scsiDev.resetFlag))\r
720                 {\r
721                         if (!scsiStatusSEL())\r
722                         {\r
723                                 break;\r
724                         }\r
725                         else if (s2s_elapsedTime_ms(selTimerBegin) >= 10 &&\r
726                                 scsiDev.target->cfg->quirks == S2S_CFG_QUIRKS_XEBEC)\r
727                         {\r
728                                 // XEBEC hosts may not bother releasing SEL at all until\r
729                                 // just before the command ends.\r
730                                 break;\r
731                         }\r
732                         else if (s2s_elapsedTime_ms(selTimerBegin) >= 250)\r
733                         {\r
734                                 *SCSI_CTRL_BSY = 0;\r
735                                 scsiDev.resetFlag = 1;\r
736                                 break;\r
737                         }\r
738                 }\r
739 \r
740                 scsiDev.phase = COMMAND;\r
741         }\r
742         else if (!selStatus)\r
743         {\r
744                 scsiDev.phase = BUS_BUSY;\r
745         }\r
746         scsiDev.selFlag = 0;\r
747 }\r
748 \r
749 static void process_MessageOut()\r
750 {\r
751         int wasNeedSyncNegotiationAck = scsiDev.needSyncNegotiationAck;\r
752         scsiDev.needSyncNegotiationAck = 0; // Successful on -most- messages.\r
753 \r
754         scsiEnterPhase(MESSAGE_OUT);\r
755 \r
756         scsiDev.atnFlag = 0;\r
757         scsiDev.msgOut = scsiReadByte();\r
758         scsiDev.msgCount++;\r
759 \r
760         if (scsiParityError() &&\r
761                 (scsiDev.boardCfg.flags & S2S_CFG_ENABLE_PARITY))\r
762         {\r
763                 // Skip the remaining message bytes, and then start the MESSAGE_OUT\r
764                 // phase again from the start. The initiator will re-send the\r
765                 // same set of messages.\r
766                 while (scsiStatusATN() && !scsiDev.resetFlag)\r
767                 {\r
768                         scsiReadByte();\r
769                 }\r
770 \r
771                 // Go-back and try the message again.\r
772                 scsiDev.atnFlag = 1;\r
773         }\r
774         else if (scsiDev.msgOut == 0x00)\r
775         {\r
776                 // COMMAND COMPLETE. but why would the target be receiving this ? nfi.\r
777                 enter_BusFree();\r
778         }\r
779         else if (scsiDev.msgOut == 0x06)\r
780         {\r
781                 // ABORT\r
782                 scsiDiskReset();\r
783                 enter_BusFree();\r
784         }\r
785         else if (scsiDev.msgOut == 0x0C)\r
786         {\r
787                 // BUS DEVICE RESET\r
788 \r
789                 scsiDiskReset();\r
790 \r
791                 scsiDev.target->unitAttention = SCSI_BUS_RESET;\r
792 \r
793                 // ANY initiator can reset the reservation state via this message.\r
794                 scsiDev.target->reservedId = -1;\r
795                 scsiDev.target->reserverId = -1;\r
796 \r
797                 // Cancel any sync negotiation\r
798                 scsiDev.target->syncOffset = 0;\r
799                 scsiDev.target->syncPeriod = 0;\r
800 \r
801                 enter_BusFree();\r
802         }\r
803         else if (scsiDev.msgOut == 0x05)\r
804         {\r
805                 // Initiate Detected Error\r
806                 // Ignore for now\r
807         }\r
808         else if (scsiDev.msgOut == 0x0F)\r
809         {\r
810                 // INITIATE RECOVERY\r
811                 // Ignore for now\r
812         }\r
813         else if (scsiDev.msgOut == 0x10)\r
814         {\r
815                 // RELEASE RECOVERY\r
816                 // Ignore for now\r
817                 enter_BusFree();\r
818         }\r
819         else if (scsiDev.msgOut == MSG_REJECT)\r
820         {\r
821                 // Message Reject\r
822                 // Oh well.\r
823 \r
824                 if (wasNeedSyncNegotiationAck)\r
825                 {\r
826                         scsiDev.target->syncOffset = 0;\r
827                         scsiDev.target->syncPeriod = 0;\r
828                 }\r
829         }\r
830         else if (scsiDev.msgOut == 0x08)\r
831         {\r
832                 // NOP\r
833         }\r
834         else if (scsiDev.msgOut == 0x09)\r
835         {\r
836                 // Message Parity Error\r
837                 // Go back and re-send the last message.\r
838                 scsiDev.phase = MESSAGE_IN;\r
839 \r
840                 if (wasNeedSyncNegotiationAck)\r
841                 {\r
842                         scsiDev.target->syncOffset = 0;\r
843                         scsiDev.target->syncPeriod = 0;\r
844                 }\r
845         }\r
846         else if (scsiDev.msgOut & 0x80) // 0x80 -> 0xFF\r
847         {\r
848                 // IDENTIFY\r
849                 if ((scsiDev.msgOut & 0x18) || // Reserved bits set.\r
850                         (scsiDev.msgOut & 0x20))  // We don't have any target routines!\r
851                 {\r
852                         messageReject();\r
853                 }\r
854 \r
855                 scsiDev.lun = scsiDev.msgOut & 0x7;\r
856                 scsiDev.discPriv = \r
857                         ((scsiDev.msgOut & 0x40) && (scsiDev.initiatorId >= 0))\r
858                                 ? 1 : 0;\r
859         }\r
860         else if (scsiDev.msgOut >= 0x20 && scsiDev.msgOut <= 0x2F)\r
861         {\r
862                 // Two byte message. We don't support these. read and discard.\r
863                 scsiReadByte();\r
864 \r
865                 if (scsiDev.msgOut == 0x23) {\r
866                         // Ignore Wide Residue. We're only 8 bit anyway.\r
867                 } else {\r
868                         messageReject();\r
869                 }\r
870         }\r
871         else if (scsiDev.msgOut == 0x01)\r
872         {\r
873                 int i;\r
874 \r
875                 // Extended message.\r
876                 int msgLen = scsiReadByte();\r
877                 if (msgLen == 0) msgLen = 256;\r
878                 uint8_t extmsg[256];\r
879                 for (i = 0; i < msgLen && !scsiDev.resetFlag; ++i)\r
880                 {\r
881                         // Discard bytes.\r
882                         extmsg[i] = scsiReadByte();\r
883                 }\r
884 \r
885                 if (extmsg[0] == 3 && msgLen == 2) // Wide Data Request\r
886                 {\r
887                         // Negotiate down to 8bit\r
888                         scsiEnterPhase(MESSAGE_IN);\r
889                         static const uint8_t WDTR[] = {0x01, 0x02, 0x03, 0x00};\r
890                         scsiWrite(WDTR, sizeof(WDTR));\r
891 \r
892                         // SDTR becomes invalidated.\r
893                         scsiDev.target->syncOffset = 0;\r
894                         scsiDev.target->syncPeriod = 0;\r
895                 }\r
896                 else if (extmsg[0] == 1 && msgLen == 3) // Synchronous data request\r
897                 {\r
898                         int oldPeriod = scsiDev.target->syncPeriod;\r
899                         int oldOffset = scsiDev.target->syncOffset;\r
900 \r
901                         int transferPeriod = extmsg[1];\r
902                         int offset = extmsg[2];\r
903 \r
904                         if ((\r
905                                         (transferPeriod > 0) &&\r
906                                         (transferPeriod < scsiDev.minSyncPeriod)) ||\r
907                                 (scsiDev.minSyncPeriod == 0))\r
908                         {\r
909                                 scsiDev.minSyncPeriod = transferPeriod;\r
910                         }\r
911 \r
912                         if ((transferPeriod > 80) || // 320ns, 3.125MB/s\r
913                                 // Amiga A590 (WD33C93 chip) only does 3.5MB/s sync\r
914                                 // After 80 we start to run out of bits in the fpga timing\r
915                                 // register.\r
916                                 (transferPeriod == 0) ||\r
917                                 (offset == 0) ||\r
918                                 ((scsiDev.boardCfg.scsiSpeed != S2S_CFG_SPEED_NoLimit) &&\r
919                                         (scsiDev.boardCfg.scsiSpeed <= S2S_CFG_SPEED_ASYNC_50)))\r
920                         {\r
921                                 scsiDev.target->syncOffset = 0;\r
922                                 scsiDev.target->syncPeriod = 0;\r
923                         } else {\r
924                                 scsiDev.target->syncOffset = offset <= 15 ? offset : 15;\r
925                                 // FAST20 / 50ns / 20MHz is disabled for now due to\r
926                                 // data corruption while reading data. We can count the\r
927                                 // ACK's correctly, but can't save the data to a register\r
928                                 // before it changes. (ie. transferPeriod == 12)\r
929                                 if ((scsiDev.boardCfg.scsiSpeed == S2S_CFG_SPEED_TURBO) &&\r
930                                         (transferPeriod <= 16))\r
931                                 {\r
932                                         scsiDev.target->syncPeriod = 16; // 15.6MB/s\r
933                                 }\r
934                                 else if (scsiDev.boardCfg.scsiSpeed == S2S_CFG_SPEED_TURBO)\r
935                                 {\r
936                                         scsiDev.target->syncPeriod = transferPeriod;\r
937                                 }\r
938                                 else if (transferPeriod <= 25 &&\r
939                                         ((scsiDev.boardCfg.scsiSpeed == S2S_CFG_SPEED_NoLimit) ||\r
940                                                 (scsiDev.boardCfg.scsiSpeed >= S2S_CFG_SPEED_SYNC_10)))\r
941                                 {\r
942                                         scsiDev.target->syncPeriod = 25; // 100ns, 10MB/s\r
943 \r
944                                 } else if (transferPeriod < 50 &&\r
945                                         ((scsiDev.boardCfg.scsiSpeed == S2S_CFG_SPEED_NoLimit) ||\r
946                                                 (scsiDev.boardCfg.scsiSpeed >= S2S_CFG_SPEED_SYNC_10)))\r
947                                 {\r
948                                         scsiDev.target->syncPeriod = transferPeriod;\r
949                                 } else if (transferPeriod >= 50)\r
950                                 {\r
951                                         scsiDev.target->syncPeriod = transferPeriod;\r
952                                 } else {\r
953                                         scsiDev.target->syncPeriod = 50;\r
954                                 }\r
955                         }\r
956 \r
957                         if (transferPeriod != oldPeriod ||\r
958                                 scsiDev.target->syncPeriod != oldPeriod ||\r
959                                 offset != oldOffset ||\r
960                                 scsiDev.target->syncOffset != oldOffset ||\r
961                                 !wasNeedSyncNegotiationAck) // Don't get into infinite loops negotiating.\r
962                         {\r
963                                 scsiEnterPhase(MESSAGE_IN);\r
964                                 uint8_t SDTR[] = {0x01, 0x03, 0x01, scsiDev.target->syncPeriod, scsiDev.target->syncOffset};\r
965                                 scsiWrite(SDTR, sizeof(SDTR));\r
966                                 scsiDev.needSyncNegotiationAck = 1; // Check if this message is rejected.\r
967                                 scsiDev.sdUnderrunCount = 0;  // reset counter, may work now.\r
968 \r
969                                 // Set to the theoretical speed, then adjust if we measure lower\r
970                                 // actual speeds.\r
971                                 scsiDev.hostSpeedKBs = s2s_getScsiRateKBs();\r
972                                 scsiDev.hostSpeedMeasured = 0;\r
973                         }\r
974                 }\r
975                 else\r
976                 {\r
977                         // Not supported\r
978                         messageReject();\r
979                 }\r
980         }\r
981         else\r
982         {\r
983                 messageReject();\r
984         }\r
985 \r
986         // Re-check the ATN flag in case it stays asserted.\r
987         scsiDev.atnFlag |= scsiStatusATN();\r
988 \r
989         if (!scsiDev.atnFlag)\r
990         {\r
991                 // Message wasn't rejected!\r
992                 scsiDev.needSyncNegotiationAck = 0;\r
993         }\r
994 }\r
995 \r
996 void scsiPoll(void)\r
997 {\r
998         if (resetUntil != 0 && resetUntil > s2s_getTime_ms())\r
999         {\r
1000                 return;\r
1001         }\r
1002         resetUntil = 0;\r
1003 \r
1004         if (unlikely(scsiDev.resetFlag))\r
1005         {\r
1006                 scsiReset();\r
1007                 // Still in reset phase for a few ms.\r
1008                 // Do not try and process any commands.\r
1009                 return;\r
1010         }\r
1011 \r
1012         switch (scsiDev.phase)\r
1013         {\r
1014         case BUS_FREE:\r
1015                 if (scsiStatusBSY())\r
1016                 {\r
1017                         scsiDev.phase = BUS_BUSY;\r
1018                 }\r
1019                 // The Arbitration phase is optional for SCSI1/SASI hosts if there is only\r
1020                 // one initiator in the chain. Support this by moving\r
1021                 // straight to selection if SEL is asserted.\r
1022                 // ie. the initiator won't assert BSY and it's own ID before moving to selection.\r
1023                 else if (scsiDev.selFlag || *SCSI_STS_SELECTED)\r
1024                 {\r
1025                         enter_SelectionPhase();\r
1026                 }\r
1027         break;\r
1028 \r
1029         case BUS_BUSY:\r
1030                 // Someone is using the bus. Perhaps they are trying to\r
1031                 // select us.\r
1032                 if (scsiDev.selFlag || *SCSI_STS_SELECTED)\r
1033                 {\r
1034                         enter_SelectionPhase();\r
1035                 }\r
1036                 else if (!scsiStatusBSY())\r
1037                 {\r
1038                         scsiDev.phase = BUS_FREE;\r
1039                 }\r
1040         break;\r
1041 \r
1042         case ARBITRATION:\r
1043                 // TODO Support reselection.\r
1044                 break;\r
1045 \r
1046         case SELECTION:\r
1047                 process_SelectionPhase();\r
1048         break;\r
1049 \r
1050         case RESELECTION:\r
1051                 // Not currently supported!\r
1052         break;\r
1053 \r
1054         case COMMAND:\r
1055                 // Do not check ATN here. SCSI 1 & 2 initiators must set ATN\r
1056                 // and SEL together upon entering the selection phase if they\r
1057                 // want to send a message (IDENTIFY) immediately.\r
1058                 if (scsiDev.atnFlag)\r
1059                 {\r
1060                         process_MessageOut();\r
1061                 }\r
1062                 else\r
1063                 {\r
1064                         process_Command();\r
1065                 }\r
1066         break;\r
1067 \r
1068         case DATA_IN:\r
1069                 scsiDev.atnFlag |= scsiStatusATN();\r
1070                 if (scsiDev.atnFlag)\r
1071                 {\r
1072                         process_MessageOut();\r
1073                 }\r
1074                 else\r
1075                 {\r
1076                         process_DataIn();\r
1077                 }\r
1078         break;\r
1079 \r
1080         case DATA_OUT:\r
1081                 scsiDev.atnFlag |= scsiStatusATN();\r
1082                 if (scsiDev.atnFlag)\r
1083                 {\r
1084                         process_MessageOut();\r
1085                 }\r
1086                 else\r
1087                 {\r
1088                         process_DataOut();\r
1089                 }\r
1090         break;\r
1091 \r
1092         case STATUS:\r
1093                 scsiDev.atnFlag |= scsiStatusATN();\r
1094                 if (scsiDev.atnFlag)\r
1095                 {\r
1096                         process_MessageOut();\r
1097                 }\r
1098                 else\r
1099                 {\r
1100                         process_Status();\r
1101                 }\r
1102         break;\r
1103 \r
1104         case MESSAGE_IN:\r
1105                 scsiDev.atnFlag |= scsiStatusATN();\r
1106                 if (scsiDev.atnFlag)\r
1107                 {\r
1108                         process_MessageOut();\r
1109                 }\r
1110                 else\r
1111                 {\r
1112                         process_MessageIn(1);\r
1113                 }\r
1114 \r
1115         break;\r
1116 \r
1117         case MESSAGE_OUT:\r
1118                 process_MessageOut();\r
1119         break;\r
1120         }\r
1121 }\r
1122 \r
1123 void scsiInit()\r
1124 {\r
1125         static int firstInit = 1;\r
1126 \r
1127         scsiDev.atnFlag = 0;\r
1128         scsiDev.resetFlag = 1;\r
1129         scsiDev.selFlag = 0;\r
1130         scsiDev.phase = BUS_FREE;\r
1131         scsiDev.target = NULL;\r
1132         scsiDev.compatMode = COMPAT_UNKNOWN;\r
1133         scsiDev.hostSpeedKBs = 0;\r
1134         scsiDev.hostSpeedMeasured = 0;\r
1135 \r
1136         int i;\r
1137         for (i = 0; i < S2S_MAX_TARGETS; ++i)\r
1138         {\r
1139                 const S2S_TargetCfg* cfg = s2s_getConfigByIndex(i);\r
1140                 if (cfg && (cfg->scsiId & S2S_CFG_TARGET_ENABLED))\r
1141                 {\r
1142                         scsiDev.targets[i].targetId = cfg->scsiId & S2S_CFG_TARGET_ID_BITS;\r
1143                         scsiDev.targets[i].cfg = cfg;\r
1144 \r
1145                         scsiDev.targets[i].liveCfg.bytesPerSector = cfg->bytesPerSector;\r
1146                 }\r
1147                 else\r
1148                 {\r
1149                         scsiDev.targets[i].targetId = 0xff;\r
1150                         scsiDev.targets[i].cfg = NULL;\r
1151                 }\r
1152                 scsiDev.targets[i].reservedId = -1;\r
1153                 scsiDev.targets[i].reserverId = -1;\r
1154                 if (firstInit)\r
1155                 {\r
1156                         scsiDev.targets[i].unitAttention = POWER_ON_RESET;\r
1157                 }\r
1158                 else\r
1159                 {\r
1160                         scsiDev.targets[i].unitAttention = PARAMETERS_CHANGED;\r
1161                 }\r
1162                 scsiDev.targets[i].sense.code = NO_SENSE;\r
1163                 scsiDev.targets[i].sense.asc = NO_ADDITIONAL_SENSE_INFORMATION;\r
1164 \r
1165                 scsiDev.targets[i].syncOffset = 0;\r
1166                 scsiDev.targets[i].syncPeriod = 0;\r
1167         }\r
1168         firstInit = 0;\r
1169 }\r
1170 \r
1171 /* TODO REENABLE\r
1172 void scsiDisconnect()\r
1173 {\r
1174         scsiEnterPhase(MESSAGE_IN);\r
1175         scsiWriteByte(0x02); // save data pointer\r
1176         scsiWriteByte(0x04); // disconnect msg.\r
1177 \r
1178         // For now, the caller is responsible for tracking the disconnected\r
1179         // state, and calling scsiReconnect.\r
1180         // Ideally the client would exit their loop and we'd implement this\r
1181         // as part of scsiPoll\r
1182         int phase = scsiDev.phase;\r
1183         enter_BusFree();\r
1184         scsiDev.phase = phase;\r
1185 }\r
1186 */\r
1187 \r
1188 /* TODO REENABLE\r
1189 int scsiReconnect()\r
1190 {\r
1191         int reconnected = 0;\r
1192 \r
1193         int sel = SCSI_ReadFilt(SCSI_Filt_SEL);\r
1194         int bsy = SCSI_ReadFilt(SCSI_Filt_BSY);\r
1195         if (!sel && !bsy)\r
1196         {\r
1197                 s2s_delay_us(1);\r
1198                 sel = SCSI_ReadFilt(SCSI_Filt_SEL);\r
1199                 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);\r
1200         }\r
1201 \r
1202         if (!sel && !bsy)\r
1203         {\r
1204                 // Arbitrate.\r
1205                 s2s_ledOn();\r
1206                 uint8_t scsiIdMask = 1 << scsiDev.target->targetId;\r
1207                 SCSI_Out_Bits_Write(scsiIdMask);\r
1208                 SCSI_Out_Ctl_Write(1); // Write bits manually.\r
1209                 SCSI_SetPin(SCSI_Out_BSY);\r
1210 \r
1211                 s2s_delay_us(3); // arbitrate delay. 2.4us.\r
1212 \r
1213                 uint8_t dbx = scsiReadDBxPins();\r
1214                 sel = SCSI_ReadFilt(SCSI_Filt_SEL);\r
1215                 if (sel || ((dbx ^ scsiIdMask) > scsiIdMask))\r
1216                 {\r
1217                         // Lost arbitration.\r
1218                         SCSI_Out_Ctl_Write(0);\r
1219                         SCSI_ClearPin(SCSI_Out_BSY);\r
1220                         s2s_ledOff();\r
1221                 }\r
1222                 else\r
1223                 {\r
1224                         // Won arbitration\r
1225                         SCSI_SetPin(SCSI_Out_SEL);\r
1226                         s2s_delay_us(1); // Bus clear + Bus settle.\r
1227 \r
1228                         // Reselection phase\r
1229                         SCSI_CTL_PHASE_Write(__scsiphase_io);\r
1230                         SCSI_Out_Bits_Write(scsiIdMask | (1 << scsiDev.initiatorId));\r
1231                         scsiDeskewDelay(); // 2 deskew delays\r
1232                         scsiDeskewDelay(); // 2 deskew delays\r
1233                         SCSI_ClearPin(SCSI_Out_BSY);\r
1234                         s2s_delay_us(1);  // Bus Settle Delay\r
1235 \r
1236                         uint32_t waitStart_ms = getTime_ms();\r
1237                         bsy = SCSI_ReadFilt(SCSI_Filt_BSY);\r
1238                         // Wait for initiator.\r
1239                         while (\r
1240                                 !bsy &&\r
1241                                 !scsiDev.resetFlag &&\r
1242                                 (elapsedTime_ms(waitStart_ms) < 250))\r
1243                         {\r
1244                                 bsy = SCSI_ReadFilt(SCSI_Filt_BSY);\r
1245                         }\r
1246 \r
1247                         if (bsy)\r
1248                         {\r
1249                                 SCSI_SetPin(SCSI_Out_BSY);\r
1250                                 scsiDeskewDelay(); // 2 deskew delays\r
1251                                 scsiDeskewDelay(); // 2 deskew delays\r
1252                                 SCSI_ClearPin(SCSI_Out_SEL);\r
1253 \r
1254                                 // Prepare for the initial IDENTIFY message.\r
1255                                 SCSI_Out_Ctl_Write(0);\r
1256                                 scsiEnterPhase(MESSAGE_IN);\r
1257 \r
1258                                 // Send identify command\r
1259                                 scsiWriteByte(0x80);\r
1260 \r
1261                                 scsiEnterPhase(scsiDev.phase);\r
1262                                 reconnected = 1;\r
1263                         }\r
1264                         else\r
1265                         {\r
1266                                 // reselect timeout.\r
1267                                 SCSI_Out_Ctl_Write(0);\r
1268                                 SCSI_ClearPin(SCSI_Out_SEL);\r
1269                                 SCSI_CTL_PHASE_Write(0);\r
1270                                 s2s_ledOff();\r
1271                         }\r
1272                 }\r
1273         }\r
1274         return reconnected;\r
1275 }\r
1276 */\r
1277 \r