d51b3c0db198e7b0636ce223e1669cc5c14c18df
[SCSI2SD-V6.git] / src / firmware / 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 \r
18 #include "scsi.h"\r
19 #include "config.h"\r
20 #include "inquiry.h"\r
21 \r
22 #include <string.h>\r
23 \r
24 static uint8_t StandardResponse[] =\r
25 {\r
26 0x00, // "Direct-access device". AKA standard hard disk\r
27 0x00, // device type modifier\r
28 0x02, // Complies with ANSI SCSI-2.\r
29 0x01, // Response format is compatible with the old CCS format.\r
30 0x1f, // standard length.\r
31 0, 0, // Reserved\r
32 0x18 // Enable sync and linked commands\r
33 };\r
34 // Vendor set by config 'c','o','d','e','s','r','c',' ',\r
35 // prodId set by config'S','C','S','I','2','S','D',' ',' ',' ',' ',' ',' ',' ',' ',' ',\r
36 // Revision set by config'2','.','0','a'\r
37 \r
38 \r
39 static const uint8_t SupportedVitalPages[] =\r
40 {\r
41 0x00, // "Direct-access device". AKA standard hard disk\r
42 0x00, // Page Code\r
43 0x00, // Reserved\r
44 0x04, // Page length\r
45 0x00, // Support "Supported vital product data pages"\r
46 0x80, // Support "Unit serial number page"\r
47 0x81, // Support "Implemented operating definition page"\r
48 0x82 // Support "ASCII Implemented operating definition page"\r
49 };\r
50 \r
51 static const uint8_t UnitSerialNumber[] =\r
52 {\r
53 0x00, // "Direct-access device". AKA standard hard disk\r
54 0x80, // Page Code\r
55 0x00, // Reserved\r
56 0x10, // Page length\r
57 'c','o','d','e','s','r','c','-','1','2','3','4','5','6','7','8'\r
58 };\r
59 \r
60 static const uint8_t ImpOperatingDefinition[] =\r
61 {\r
62 0x00, // "Direct-access device". AKA standard hard disk\r
63 0x81, // Page Code\r
64 0x00, // Reserved\r
65 0x03, // Page length\r
66 0x03, // Current: SCSI-2 operating definition\r
67 0x03, // Default: SCSI-2 operating definition\r
68 0x03 // Supported (list): SCSI-2 operating definition.\r
69 };\r
70 \r
71 static const uint8_t AscImpOperatingDefinition[] =\r
72 {\r
73 0x00, // "Direct-access device". AKA standard hard disk\r
74 0x82, // Page Code\r
75 0x00, // Reserved\r
76 0x07, // Page length\r
77 0x06, // Ascii length\r
78 'S','C','S','I','-','2'\r
79 };\r
80 \r
81 void s2s_scsiInquiry()\r
82 {\r
83         uint8_t evpd = scsiDev.cdb[1] & 1; // enable vital product data.\r
84         uint8_t pageCode = scsiDev.cdb[2];\r
85         uint32_t allocationLength = scsiDev.cdb[4];\r
86 \r
87         // SASI standard, X3T9.3_185_RevE  states that 0 == 256 bytes\r
88         if (allocationLength == 0) allocationLength = 256;\r
89 \r
90         if (!evpd)\r
91         {\r
92                 if (pageCode)\r
93                 {\r
94                         // error.\r
95                         scsiDev.status = CHECK_CONDITION;\r
96                         scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
97                         scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
98                         scsiDev.phase = STATUS;\r
99                 }\r
100                 else\r
101                 {\r
102                         const S2S_TargetCfg* config = scsiDev.target->cfg;\r
103                         scsiDev.dataLen =\r
104                                 s2s_getStandardInquiry(\r
105                                         config,\r
106                                         scsiDev.data,\r
107                                         sizeof(scsiDev.data));\r
108                         scsiDev.phase = DATA_IN;\r
109                 }\r
110         }\r
111         else if (pageCode == 0x00)\r
112         {\r
113                 memcpy(scsiDev.data, SupportedVitalPages, sizeof(SupportedVitalPages));\r
114                 scsiDev.dataLen = sizeof(SupportedVitalPages);\r
115                 scsiDev.phase = DATA_IN;\r
116         }\r
117         else if (pageCode == 0x80)\r
118         {\r
119                 memcpy(scsiDev.data, UnitSerialNumber, sizeof(UnitSerialNumber));\r
120                 scsiDev.dataLen = sizeof(UnitSerialNumber);\r
121                 scsiDev.phase = DATA_IN;\r
122         }\r
123         else if (pageCode == 0x81)\r
124         {\r
125                 memcpy(\r
126                         scsiDev.data,\r
127                         ImpOperatingDefinition,\r
128                         sizeof(ImpOperatingDefinition));\r
129                 scsiDev.dataLen = sizeof(ImpOperatingDefinition);\r
130                 scsiDev.phase = DATA_IN;\r
131         }\r
132         else if (pageCode == 0x82)\r
133         {\r
134                 memcpy(\r
135                         scsiDev.data,\r
136                         AscImpOperatingDefinition,\r
137                         sizeof(AscImpOperatingDefinition));\r
138                 scsiDev.dataLen = sizeof(AscImpOperatingDefinition);\r
139                 scsiDev.phase = DATA_IN;\r
140         }\r
141         else\r
142         {\r
143                 // error.\r
144                 scsiDev.status = CHECK_CONDITION;\r
145                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
146                 scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
147                 scsiDev.phase = STATUS;\r
148         }\r
149 \r
150 \r
151         if (scsiDev.phase == DATA_IN)\r
152         {\r
153                 // "real" hard drives send back exactly allocationLenth bytes, padded\r
154                 // with zeroes. This only seems to happen for Inquiry responses, and not\r
155                 // other commands that also supply an allocation length such as Mode Sense or\r
156                 // Request Sense.\r
157                 // (See below for exception to this rule when 0 allocation length)\r
158                 if (scsiDev.dataLen < allocationLength)\r
159                 {\r
160                         memset(\r
161                                 &scsiDev.data[scsiDev.dataLen],\r
162                                 0,\r
163                                 allocationLength - scsiDev.dataLen);\r
164                 }\r
165                 // Spec 8.2.5 requires us to simply truncate the response if it's\r
166                 // too big.\r
167                 scsiDev.dataLen = allocationLength;\r
168 \r
169                 // Set the device type as needed.\r
170                 switch (scsiDev.target->cfg->deviceType)\r
171                 {\r
172                 case S2S_CFG_OPTICAL:\r
173                         scsiDev.data[0] = 0x05; // device type\r
174                         scsiDev.data[1] |= 0x80; // Removable bit.\r
175                         break;\r
176 \r
177                 case S2S_CFG_SEQUENTIAL:\r
178                         scsiDev.data[0] = 0x01; // device type\r
179                         scsiDev.data[1] |= 0x80; // Removable bit.\r
180                         break;\r
181 \r
182                 case S2S_CFG_MO:\r
183                         scsiDev.data[0] = 0x07; // device type\r
184                         scsiDev.data[1] |= 0x80; // Removable bit.\r
185                         break;\r
186 \r
187                 case S2S_CFG_FLOPPY_14MB:\r
188                 case S2S_CFG_REMOVEABLE:\r
189                         scsiDev.data[1] |= 0x80; // Removable bit.\r
190                         break;\r
191                 default:\r
192                         // Accept defaults for a fixed disk.\r
193                         break;\r
194                 }\r
195         }\r
196 \r
197         // Set the first byte to indicate LUN presence.\r
198         if (scsiDev.lun) // We only support lun 0\r
199         {\r
200                 scsiDev.data[0] = 0x7F;\r
201         }\r
202 }\r
203 \r
204 uint32_t s2s_getStandardInquiry(\r
205         const S2S_TargetCfg* cfg, uint8_t* out, uint32_t maxlen\r
206         )\r
207 {\r
208         uint32_t buflen = sizeof(StandardResponse);\r
209         if (buflen > maxlen) buflen = maxlen;\r
210 \r
211         memcpy(out, StandardResponse, buflen);\r
212         out[1] = cfg->deviceTypeModifier;\r
213         memcpy(&out[8], cfg->vendor, sizeof(cfg->vendor));\r
214         memcpy(&out[16], cfg->prodId, sizeof(cfg->prodId));\r
215         memcpy(&out[32], cfg->revision, sizeof(cfg->revision));\r
216         return sizeof(StandardResponse) +\r
217                 sizeof(cfg->vendor) +\r
218                 sizeof(cfg->prodId) +\r
219                 sizeof(cfg->revision);\r
220 }\r