Use DMA for SCSI and SD card transfers for a massive performance boost.
[SCSI2SD-V6.git] / software / SCSI2SD / src / diagnostic.c
1 //      Copyright (C) 2013 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 "diagnostic.h"\r
21 \r
22 #include <string.h>\r
23 \r
24 static const uint8 SupportedDiagnosticPages[] =\r
25 {\r
26 0x00, // Page Code\r
27 0x00, // Reserved\r
28 0x02, // Page length\r
29 0x00, // Support "Supported diagnostic page"\r
30 0x40  // Support "Translate address page"\r
31 };\r
32 \r
33 void scsiSendDiagnostic()\r
34 {\r
35         // SEND DIAGNOSTIC\r
36         // Pretend to do self-test. Actual data is returned via the\r
37         // RECEIVE DIAGNOSTIC RESULTS command.\r
38         int selfTest = scsiDev.cdb[1] & 0x04;\r
39         uint32 paramLength =\r
40                 (((uint32) scsiDev.cdb[3]) << 8) +\r
41                 scsiDev.cdb[4];\r
42 \r
43         if (!selfTest)\r
44         {\r
45                 // Initiator sends us page data.\r
46                 scsiDev.dataLen = paramLength;\r
47                 scsiDev.phase = DATA_OUT;\r
48 \r
49                 if (scsiDev.dataLen > sizeof (scsiDev.data))\r
50                 {\r
51                         // Nowhere to store this data!\r
52                         // Shouldn't happen - our buffer should be many magnitudes larger\r
53                         // than the required size for diagnostic parameters.\r
54                         scsiDev.sense.code = ILLEGAL_REQUEST;\r
55                         scsiDev.sense.asc = INVALID_FIELD_IN_CDB;\r
56                         scsiDev.status = CHECK_CONDITION;\r
57                         scsiDev.phase = STATUS;\r
58                 }\r
59         }\r
60         else\r
61         {\r
62                 // Default command result will be a status of GOOD anyway.\r
63         }\r
64 }\r
65 \r
66 void scsiReceiveDiagnostic()\r
67 {\r
68         // RECEIVE DIAGNOSTIC RESULTS\r
69         // We assume scsiDev.data contains the contents of a previous\r
70         // SEND DIAGNOSTICS command.  We only care about the page-code part\r
71         // of the parameter list.\r
72         uint8 pageCode = scsiDev.data[0];\r
73 \r
74         int allocLength =\r
75                 (((uint16) scsiDev.cdb[3]) << 8) +\r
76                 scsiDev.cdb[4];\r
77 \r
78 \r
79         if (pageCode == 0x00)\r
80         {\r
81                 memcpy(\r
82                         scsiDev.data,\r
83                         SupportedDiagnosticPages,\r
84                         sizeof(SupportedDiagnosticPages));\r
85                 scsiDev.dataLen = sizeof(SupportedDiagnosticPages);\r
86                 scsiDev.phase = DATA_IN;\r
87         }\r
88         else if (pageCode == 0x40)\r
89         {\r
90                 // Translate between logical block address, physical sector address, or\r
91                 // physical bytes.\r
92                 uint8 suppliedFmt = scsiDev.data[4] & 0x7;\r
93                 uint8 translateFmt = scsiDev.data[5] & 0x7;\r
94 \r
95                 // Convert each supplied address back to a simple\r
96                 // 64bit linear address, then convert back again.\r
97                 uint64 fromByteAddr =\r
98                         scsiByteAddress(suppliedFmt, &scsiDev.data[6]);\r
99 \r
100                 scsiSaveByteAddress(translateFmt, fromByteAddr, &scsiDev.data[6]);\r
101 \r
102                 // Fill out the rest of the response.\r
103                 // (Clear out any optional bits).\r
104                 scsiDev.data[4] = suppliedFmt;\r
105                 scsiDev.data[5] = translateFmt;\r
106 \r
107                 scsiDev.dataLen = 14;\r
108                 scsiDev.phase = DATA_IN;\r
109         }\r
110         else\r
111         {\r
112                 // error.\r
113                 scsiDev.status = CHECK_CONDITION;\r
114                 scsiDev.sense.code = ILLEGAL_REQUEST;\r
115                 scsiDev.sense.asc = INVALID_FIELD_IN_CDB;\r
116                 scsiDev.phase = STATUS;\r
117         }\r
118 \r
119         if (scsiDev.phase == DATA_IN && scsiDev.dataLen > allocLength)\r
120         {\r
121                 // simply truncate the response.\r
122                 scsiDev.dataLen = allocLength;\r
123         }\r
124 \r
125         {\r
126                 uint8 lun = scsiDev.cdb[1] >> 5;\r
127                 // Set the first byte to indicate LUN presence.\r
128                 if (lun) // We only support lun 0\r
129                 {\r
130                         scsiDev.data[0] = 0x7F;\r
131                 }\r
132         }\r
133 }\r
134 \r
135 void scsiReadBuffer()\r
136 {\r
137         // READ BUFFER\r
138         // Used for testing the speed of the SCSI interface.\r
139         uint8 mode = scsiDev.data[1] & 7;\r
140         \r
141         int allocLength =\r
142                 (((uint32) scsiDev.cdb[6]) << 16) +\r
143                 (((uint32) scsiDev.cdb[7]) << 8) +\r
144                 scsiDev.cdb[8];\r
145 \r
146         if (mode == 0)\r
147         {\r
148                 uint32_t maxSize = MAX_SECTOR_SIZE - 4;\r
149                 // 4 byte header\r
150                 scsiDev.data[0] = 0;\r
151                 scsiDev.data[1] = (maxSize >> 16) & 0xff;\r
152                 scsiDev.data[2] = (maxSize >> 8) & 0xff;\r
153                 scsiDev.data[3] = maxSize & 0xff;\r
154                 \r
155                 scsiDev.dataLen =\r
156                         (allocLength > MAX_SECTOR_SIZE) ? MAX_SECTOR_SIZE : allocLength;\r
157                 scsiDev.phase = DATA_IN;\r
158         }\r
159 }\r