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