Fix for VMS 5.5-2 for incorrect Inquiry command allocation lengths
[SCSI2SD-V6.git] / src / firmware / inquiry.c
1 //      Copyright (C) 2013 Michael McMaster <michael@codesrc.com>\r
2 //      Copyright (C) 2019 Landon Rodgers  <g.landon.rodgers@gmail.com>\r
3 //\r
4 //      This file is part of SCSI2SD.\r
5 //\r
6 //      SCSI2SD is free software: you can redistribute it and/or modify\r
7 //      it under the terms of the GNU General Public License as published by\r
8 //      the Free Software Foundation, either version 3 of the License, or\r
9 //      (at your option) any later version.\r
10 //\r
11 //      SCSI2SD is distributed in the hope that it will be useful,\r
12 //      but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 //      GNU General Public License for more details.\r
15 //\r
16 //      You should have received a copy of the GNU General Public License\r
17 //      along with SCSI2SD.  If not, see <http://www.gnu.org/licenses/>.\r
18 \r
19 #include "scsi.h"\r
20 #include "config.h"\r
21 #include "inquiry.h"\r
22 \r
23 #include <string.h>\r
24 \r
25 static uint8_t StandardResponse[] =\r
26 {\r
27 0x00, // "Direct-access device". AKA standard hard disk\r
28 0x00, // device type modifier\r
29 0x02, // Complies with ANSI SCSI-2.\r
30 0x01, // Response format is compatible with the old CCS format.\r
31 0x1f, // standard length.\r
32 0, 0, // Reserved\r
33 0x18 // Enable sync and linked commands\r
34 };\r
35 // Vendor set by config 'c','o','d','e','s','r','c',' ',\r
36 // prodId set by config'S','C','S','I','2','S','D',' ',' ',' ',' ',' ',' ',' ',' ',' ',\r
37 // Revision set by config'2','.','0','a'\r
38 \r
39 \r
40 static const uint8_t SupportedVitalPages[] =\r
41 {\r
42 0x00, // "Direct-access device". AKA standard hard disk\r
43 0x00, // Page Code\r
44 0x00, // Reserved\r
45 0x04, // Page length\r
46 0x00, // Support "Supported vital product data pages"\r
47 0x80, // Support "Unit serial number page"\r
48 0x81, // Support "Implemented operating definition page"\r
49 0x82 // Support "ASCII Implemented operating definition page"\r
50 };\r
51 \r
52 static const uint8_t UnitSerialNumber[] =\r
53 {\r
54 0x00, // "Direct-access device". AKA standard hard disk\r
55 0x80, // Page Code\r
56 0x00, // Reserved\r
57 0x10, // Page length\r
58 'c','o','d','e','s','r','c','-','1','2','3','4','5','6','7','8'\r
59 };\r
60 \r
61 static const uint8_t ImpOperatingDefinition[] =\r
62 {\r
63 0x00, // "Direct-access device". AKA standard hard disk\r
64 0x81, // Page Code\r
65 0x00, // Reserved\r
66 0x03, // Page length\r
67 0x03, // Current: SCSI-2 operating definition\r
68 0x03, // Default: SCSI-2 operating definition\r
69 0x03 // Supported (list): SCSI-2 operating definition.\r
70 };\r
71 \r
72 static const uint8_t AscImpOperatingDefinition[] =\r
73 {\r
74 0x00, // "Direct-access device". AKA standard hard disk\r
75 0x82, // Page Code\r
76 0x00, // Reserved\r
77 0x07, // Page length\r
78 0x06, // Ascii length\r
79 'S','C','S','I','-','2'\r
80 };\r
81 \r
82 void s2s_scsiInquiry()\r
83 {\r
84         uint8_t evpd = scsiDev.cdb[1] & 1; // enable vital product data.\r
85         uint8_t pageCode = scsiDev.cdb[2];\r
86         uint32_t allocationLength = scsiDev.cdb[4];\r
87 \r
88         // SASI standard, X3T9.3_185_RevE  states that 0 == 256 bytes\r
89         // BUT SCSI 2 standard says 0 == 0.\r
90         if (scsiDev.compatMode <= COMPAT_SCSI1) // excludes COMPAT_SCSI2_DISABLED\r
91         {\r
92                 if (allocationLength == 0) allocationLength = 256;\r
93         }\r
94 \r
95         if (!evpd)\r
96         {\r
97                 if (pageCode)\r
98                 {\r
99                         // error.\r
100                         scsiDev.status = CHECK_CONDITION;\r
101                         scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
102                         scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
103                         scsiDev.phase = STATUS;\r
104                 }\r
105                 else\r
106                 {\r
107                         const S2S_TargetCfg* config = scsiDev.target->cfg;\r
108                         scsiDev.dataLen =\r
109                                 s2s_getStandardInquiry(\r
110                                         config,\r
111                                         scsiDev.data,\r
112                                         sizeof(scsiDev.data));\r
113                         scsiDev.phase = DATA_IN;\r
114                 }\r
115         }\r
116         else if (pageCode == 0x00)\r
117         {\r
118                 memcpy(scsiDev.data, SupportedVitalPages, sizeof(SupportedVitalPages));\r
119                 scsiDev.dataLen = sizeof(SupportedVitalPages);\r
120                 scsiDev.phase = DATA_IN;\r
121         }\r
122         else if (pageCode == 0x80)\r
123         {\r
124                 memcpy(scsiDev.data, UnitSerialNumber, sizeof(UnitSerialNumber));\r
125                 scsiDev.dataLen = sizeof(UnitSerialNumber);\r
126                 scsiDev.phase = DATA_IN;\r
127         }\r
128         else if (pageCode == 0x81)\r
129         {\r
130                 memcpy(\r
131                         scsiDev.data,\r
132                         ImpOperatingDefinition,\r
133                         sizeof(ImpOperatingDefinition));\r
134                 scsiDev.dataLen = sizeof(ImpOperatingDefinition);\r
135                 scsiDev.phase = DATA_IN;\r
136         }\r
137         else if (pageCode == 0x82)\r
138         {\r
139                 memcpy(\r
140                         scsiDev.data,\r
141                         AscImpOperatingDefinition,\r
142                         sizeof(AscImpOperatingDefinition));\r
143                 scsiDev.dataLen = sizeof(AscImpOperatingDefinition);\r
144                 scsiDev.phase = DATA_IN;\r
145         }\r
146         else\r
147         {\r
148                 // error.\r
149                 scsiDev.status = CHECK_CONDITION;\r
150                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
151                 scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
152                 scsiDev.phase = STATUS;\r
153         }\r
154 \r
155 \r
156         if (scsiDev.phase == DATA_IN)\r
157         {\r
158                 // VAX workaround\r
159                 if (allocationLength == 255 &&\r
160                         (scsiDev.target->cfg->quirks & S2S_CFG_QUIRKS_VMS))\r
161                 {\r
162                         allocationLength = 254;\r
163                 }\r
164 \r
165                 // "real" hard drives send back exactly allocationLenth bytes, padded\r
166                 // with zeroes. This only seems to happen for Inquiry responses, and not\r
167                 // other commands that also supply an allocation length such as Mode Sense or\r
168                 // Request Sense.\r
169                 // (See below for exception to this rule when 0 allocation length)\r
170                 if (scsiDev.dataLen < allocationLength)\r
171                 {\r
172                         memset(\r
173                                 &scsiDev.data[scsiDev.dataLen],\r
174                                 0,\r
175                                 allocationLength - scsiDev.dataLen);\r
176                 }\r
177                 // Spec 8.2.5 requires us to simply truncate the response if it's\r
178                 // too big.\r
179                 scsiDev.dataLen = allocationLength;\r
180 \r
181                 // Set the device type as needed.\r
182                 scsiDev.data[0] = getDeviceTypeQualifier();\r
183 \r
184                 switch (scsiDev.target->cfg->deviceType)\r
185                 {\r
186                 case S2S_CFG_OPTICAL:\r
187                         scsiDev.data[1] |= 0x80; // Removable bit.\r
188                         break;\r
189 \r
190                 case S2S_CFG_SEQUENTIAL:\r
191                         scsiDev.data[1] |= 0x80; // Removable bit.\r
192                         break;\r
193 \r
194                 case S2S_CFG_MO:\r
195                         scsiDev.data[1] |= 0x80; // Removable bit.\r
196                         break;\r
197 \r
198                 case S2S_CFG_FLOPPY_14MB:\r
199                 case S2S_CFG_REMOVEABLE:\r
200                         scsiDev.data[1] |= 0x80; // Removable bit.\r
201                         break;\r
202                 default:\r
203                         // Accept defaults for a fixed disk.\r
204                         break;\r
205                 }\r
206         }\r
207 \r
208         // Set the first byte to indicate LUN presence.\r
209         if (scsiDev.lun) // We only support lun 0\r
210         {\r
211                 scsiDev.data[0] = 0x7F;\r
212         }\r
213 }\r
214 \r
215 uint32_t s2s_getStandardInquiry(\r
216         const S2S_TargetCfg* cfg, uint8_t* out, uint32_t maxlen\r
217         )\r
218 {\r
219         uint32_t buflen = sizeof(StandardResponse);\r
220         if (buflen > maxlen) buflen = maxlen;\r
221 \r
222         memcpy(out, StandardResponse, buflen);\r
223         out[1] = cfg->deviceTypeModifier;\r
224 \r
225         if (scsiDev.compatMode >= COMPAT_SCSI2)\r
226         {\r
227                 out[3] = 2; // SCSI 2 response format.\r
228         }\r
229         memcpy(&out[8], cfg->vendor, sizeof(cfg->vendor));\r
230         memcpy(&out[16], cfg->prodId, sizeof(cfg->prodId));\r
231         memcpy(&out[32], cfg->revision, sizeof(cfg->revision));\r
232         return sizeof(StandardResponse) +\r
233                 sizeof(cfg->vendor) +\r
234                 sizeof(cfg->prodId) +\r
235                 sizeof(cfg->revision);\r
236 }\r
237 \r
238 uint8_t getDeviceTypeQualifier()\r
239 {\r
240         // Set the device type as needed.\r
241         switch (scsiDev.target->cfg->deviceType)\r
242         {\r
243         case S2S_CFG_OPTICAL:\r
244                 return 0x05;\r
245                 break;\r
246 \r
247         case S2S_CFG_SEQUENTIAL:\r
248                 return 0x01;\r
249                 break;\r
250 \r
251         case S2S_CFG_MO:\r
252                 return 0x07;\r
253                 break;\r
254 \r
255         case S2S_CFG_FLOPPY_14MB:\r
256         case S2S_CFG_REMOVEABLE:\r
257                 return 0;\r
258                 break;\r
259 \r
260         default:\r
261                 // Accept defaults for a fixed disk.\r
262                 return 0;\r
263         }\r
264 }\r
265 \r