Merge branch 'master' of ssh://webhost.codesrc.com/home/michael/projects/SCSI2SD
[SCSI2SD.git] / software / SCSI2SD / src / inquiry.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 #pragma GCC push_options\r
18 #pragma GCC optimize("-flto")\r
19 \r
20 #include "device.h"\r
21 #include "scsi.h"\r
22 #include "config.h"\r
23 #include "inquiry.h"\r
24 \r
25 #include <string.h>\r
26 \r
27 static uint8 StandardResponse[] =\r
28 {\r
29 0x00, // "Direct-access device". AKA standard hard disk\r
30 0x00, // device type modifier\r
31 0x02, // Complies with ANSI SCSI-2.\r
32 0x01, // Response format is compatible with the old CCS format.\r
33 0x1f, // standard length.\r
34 0, 0, // Reserved\r
35 0x08 // Enable linked commands\r
36 };\r
37 // Vendor set by config 'c','o','d','e','s','r','c',' ',\r
38 // prodId set by config'S','C','S','I','2','S','D',' ',' ',' ',' ',' ',' ',' ',' ',' ',\r
39 // Revision set by config'2','.','0','a'\r
40 \r
41 /* For reference, here's a dump from an Apple branded 500Mb drive from 1994.\r
42 $ sudo sg_inq -H /dev/sdd --len 255\r
43 standard INQUIRY:\r
44  00     00 00 02 01 31 00 00 18  51 55 41 4e 54 55 4d 20    ....1...QUANTUM \r
45  10     4c 50 53 32 37 30 20 20  20 20 20 20 20 20 20 20    LPS270          \r
46  20     30 39 30 30 00 00 00 d9  b0 27 34 01 04 b3 01 1b    0900.....'4.....\r
47  30     07 00 a0 00 00 ff                                   ......\r
48  Vendor identification: QUANTUM \r
49  Product identification: LPS270          \r
50  Product revision level: 0900\r
51 */\r
52 \r
53 \r
54 static const uint8 SupportedVitalPages[] =\r
55 {\r
56 0x00, // "Direct-access device". AKA standard hard disk\r
57 0x00, // Page Code\r
58 0x00, // Reserved\r
59 0x04, // Page length\r
60 0x00, // Support "Supported vital product data pages"\r
61 0x80, // Support "Unit serial number page"\r
62 0x81, // Support "Implemented operating definition page"\r
63 0x82 // Support "ASCII Implemented operating definition page"\r
64 };\r
65 \r
66 static const uint8 UnitSerialNumber[] =\r
67 {\r
68 0x00, // "Direct-access device". AKA standard hard disk\r
69 0x80, // Page Code\r
70 0x00, // Reserved\r
71 0x10, // Page length\r
72 'c','o','d','e','s','r','c','-','1','2','3','4','5','6','7','8'\r
73 };\r
74 \r
75 static const uint8 ImpOperatingDefinition[] =\r
76 {\r
77 0x00, // "Direct-access device". AKA standard hard disk\r
78 0x81, // Page Code\r
79 0x00, // Reserved\r
80 0x03, // Page length\r
81 0x03, // Current: SCSI-2 operating definition\r
82 0x03, // Default: SCSI-2 operating definition\r
83 0x03 // Supported (list): SCSI-2 operating definition.\r
84 };\r
85 \r
86 static const uint8 AscImpOperatingDefinition[] =\r
87 {\r
88 0x00, // "Direct-access device". AKA standard hard disk\r
89 0x82, // Page Code\r
90 0x00, // Reserved\r
91 0x07, // Page length\r
92 0x06, // Ascii length\r
93 'S','C','S','I','-','2'\r
94 };\r
95 \r
96 void scsiInquiry()\r
97 {\r
98         uint8 evpd = scsiDev.cdb[1] & 1; // enable vital product data.\r
99         uint8 pageCode = scsiDev.cdb[2];\r
100         uint32 allocationLength = scsiDev.cdb[4];\r
101 \r
102         // SASI standard, X3T9.3_185_RevE  states that 0 == 256 bytes\r
103         if (allocationLength == 0) allocationLength = 256;\r
104 \r
105         if (!evpd)\r
106         {\r
107                 if (pageCode)\r
108                 {\r
109                         // error.\r
110                         scsiDev.status = CHECK_CONDITION;\r
111                         scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
112                         scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
113                         scsiDev.phase = STATUS;\r
114                 }\r
115                 else\r
116                 {\r
117                         const TargetConfig* config = scsiDev.target->cfg;\r
118                         memcpy(scsiDev.data, StandardResponse, sizeof(StandardResponse));\r
119                         scsiDev.data[1] = scsiDev.target->cfg->deviceTypeModifier;\r
120                         memcpy(&scsiDev.data[8], config->vendor, sizeof(config->vendor));\r
121                         memcpy(&scsiDev.data[16], config->prodId, sizeof(config->prodId));\r
122                         memcpy(&scsiDev.data[32], config->revision, sizeof(config->revision));\r
123                         scsiDev.dataLen = sizeof(StandardResponse) +\r
124                                 sizeof(config->vendor) +\r
125                                 sizeof(config->prodId) +\r
126                                 sizeof(config->revision);\r
127                         scsiDev.phase = DATA_IN;\r
128                 }\r
129         }\r
130         else if (pageCode == 0x00)\r
131         {\r
132                 memcpy(scsiDev.data, SupportedVitalPages, sizeof(SupportedVitalPages));\r
133                 scsiDev.dataLen = sizeof(SupportedVitalPages);\r
134                 scsiDev.phase = DATA_IN;\r
135         }\r
136         else if (pageCode == 0x80)\r
137         {\r
138                 memcpy(scsiDev.data, UnitSerialNumber, sizeof(UnitSerialNumber));\r
139                 scsiDev.dataLen = sizeof(UnitSerialNumber);\r
140                 scsiDev.phase = DATA_IN;\r
141         }\r
142         else if (pageCode == 0x81)\r
143         {\r
144                 memcpy(\r
145                         scsiDev.data,\r
146                         ImpOperatingDefinition,\r
147                         sizeof(ImpOperatingDefinition));\r
148                 scsiDev.dataLen = sizeof(ImpOperatingDefinition);\r
149                 scsiDev.phase = DATA_IN;\r
150         }\r
151         else if (pageCode == 0x82)\r
152         {\r
153                 memcpy(\r
154                         scsiDev.data,\r
155                         AscImpOperatingDefinition,\r
156                         sizeof(AscImpOperatingDefinition));\r
157                 scsiDev.dataLen = sizeof(AscImpOperatingDefinition);\r
158                 scsiDev.phase = DATA_IN;\r
159         }\r
160         else\r
161         {\r
162                 // error.\r
163                 scsiDev.status = CHECK_CONDITION;\r
164                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
165                 scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
166                 scsiDev.phase = STATUS;\r
167         }\r
168 \r
169 \r
170         if (scsiDev.phase == DATA_IN)\r
171         {\r
172                 // "real" hard drives send back exactly allocationLenth bytes, padded\r
173                 // with zeroes. This only seems to happen for Inquiry responses, and not\r
174                 // other commands that also supply an allocation length such as Mode Sense or\r
175                 // Request Sense.\r
176                 // (See below for exception to this rule when 0 allocation length)\r
177                 if (scsiDev.dataLen < allocationLength)\r
178                 {\r
179                         memset(\r
180                                 &scsiDev.data[scsiDev.dataLen],\r
181                                 0,\r
182                                 allocationLength - scsiDev.dataLen);\r
183                 }\r
184                 if (scsiDev.cdb[4] == 0 && scsiDev.dataLen < allocationLength)\r
185                 {\r
186                         // Only send back the minimum number of bytes.\r
187                         // Don't forcably send back 256 bytes, as that may cause problems\r
188                         // with some machines (SGI Iris Indigo running IRIX)\r
189                         // scsiDev.dataLen is already the correct value.\r
190                 }\r
191                 else\r
192                 {\r
193                         // Spec 8.2.5 requires us to simply truncate the response if it's\r
194                         // too big.\r
195                         scsiDev.dataLen = allocationLength;\r
196                 }\r
197 \r
198                 // Set the device type as needed.\r
199                 switch (scsiDev.target->cfg->deviceType)\r
200                 {\r
201                 case CONFIG_OPTICAL:\r
202                         scsiDev.data[0] = 0x05; // device type\r
203                         scsiDev.data[1] |= 0x80; // Removable bit.\r
204                         break;\r
205 \r
206                 case CONFIG_SEQUENTIAL:\r
207                         scsiDev.data[0] = 0x01; // device type\r
208                         scsiDev.data[1] |= 0x80; // Removable bit.\r
209                         break;\r
210                         \r
211                 case CONFIG_MO:\r
212                         scsiDev.data[0] = 0x07; // device type\r
213                         scsiDev.data[1] |= 0x80; // Removable bit.\r
214                         break;\r
215 \r
216                 case CONFIG_FLOPPY_14MB:\r
217                 case CONFIG_REMOVEABLE:\r
218                         scsiDev.data[1] |= 0x80; // Removable bit.\r
219                         break;\r
220                  default:\r
221                         // Accept defaults for a fixed disk.\r
222                         break;\r
223                 }\r
224         }\r
225 \r
226         // Set the first byte to indicate LUN presence.\r
227         if (scsiDev.lun) // We only support lun 0\r
228         {\r
229                 scsiDev.data[0] = 0x7F;\r
230         }\r
231 }\r
232 \r
233 #pragma GCC pop_options\r