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