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