Implement WRITE BUFFER and WRITE WITH VERIFY commands
[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         if (allocationLength == 0) allocationLength = 256;\r
102         \r
103         if (!evpd)\r
104         {\r
105                 if (pageCode)\r
106                 {\r
107                         // error.\r
108                         scsiDev.status = CHECK_CONDITION;\r
109                         scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
110                         scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
111                         scsiDev.phase = STATUS;\r
112                 }\r
113                 else\r
114                 {\r
115                         const TargetConfig* config = scsiDev.target->cfg;\r
116                         memcpy(scsiDev.data, StandardResponse, sizeof(StandardResponse));\r
117                         scsiDev.data[1] = scsiDev.target->cfg->deviceTypeModifier;\r
118                         memcpy(&scsiDev.data[8], config->vendor, sizeof(config->vendor));\r
119                         memcpy(&scsiDev.data[16], config->prodId, sizeof(config->prodId));\r
120                         memcpy(&scsiDev.data[32], config->revision, sizeof(config->revision));\r
121                         scsiDev.dataLen = sizeof(StandardResponse) +\r
122                                 sizeof(config->vendor) +\r
123                                 sizeof(config->prodId) +\r
124                                 sizeof(config->revision);\r
125                         scsiDev.phase = DATA_IN;\r
126                 }\r
127         }\r
128         else if (pageCode == 0x00)\r
129         {\r
130                 memcpy(scsiDev.data, SupportedVitalPages, sizeof(SupportedVitalPages));\r
131                 scsiDev.dataLen = sizeof(SupportedVitalPages);\r
132                 scsiDev.phase = DATA_IN;\r
133         }\r
134         else if (pageCode == 0x80)\r
135         {\r
136                 memcpy(scsiDev.data, UnitSerialNumber, sizeof(UnitSerialNumber));\r
137                 scsiDev.dataLen = sizeof(UnitSerialNumber);\r
138                 scsiDev.phase = DATA_IN;\r
139         }\r
140         else if (pageCode == 0x81)\r
141         {\r
142                 memcpy(\r
143                         scsiDev.data,\r
144                         ImpOperatingDefinition,\r
145                         sizeof(ImpOperatingDefinition));\r
146                 scsiDev.dataLen = sizeof(ImpOperatingDefinition);\r
147                 scsiDev.phase = DATA_IN;\r
148         }\r
149         else if (pageCode == 0x82)\r
150         {\r
151                 memcpy(\r
152                         scsiDev.data,\r
153                         AscImpOperatingDefinition,\r
154                         sizeof(AscImpOperatingDefinition));\r
155                 scsiDev.dataLen = sizeof(AscImpOperatingDefinition);\r
156                 scsiDev.phase = DATA_IN;\r
157         }\r
158         else\r
159         {\r
160                 // error.\r
161                 scsiDev.status = CHECK_CONDITION;\r
162                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
163                 scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
164                 scsiDev.phase = STATUS;\r
165         }\r
166 \r
167 \r
168         if (scsiDev.phase == DATA_IN)\r
169         {\r
170                 // "real" hard drives send back exactly allocationLenth bytes, padded\r
171                 // with zeroes. This only seems to happen for Inquiry responses, and not\r
172                 // other commands that also supply an allocation length such as Mode Sense or\r
173                 // Request Sense.\r
174                 if (scsiDev.dataLen < allocationLength)\r
175                 {\r
176                         memset(\r
177                                 &scsiDev.data[scsiDev.dataLen],\r
178                                 0,\r
179                                 allocationLength - scsiDev.dataLen);\r
180                 }\r
181                 // Spec 8.2.5 requires us to simply truncate the response if it's too big.\r
182                 scsiDev.dataLen = allocationLength;\r
183                 \r
184                 // Set the device type as needed.\r
185                 switch (scsiDev.target->cfg->deviceType)\r
186                 {\r
187                 case CONFIG_OPTICAL:\r
188                         scsiDev.data[0] = 0x05; // device type\r
189                         scsiDev.data[1] |= 0x80; // Removable bit.\r
190                         break;\r
191 \r
192                 case CONFIG_FLOPPY_14MB:\r
193                 case CONFIG_REMOVEABLE:\r
194                         scsiDev.data[1] |= 0x80; // Removable bit.\r
195                         break;\r
196                  default:\r
197                         // Accept defaults for a fixed disk.\r
198                         break;\r
199                 }\r
200         }\r
201 \r
202         // Set the first byte to indicate LUN presence.\r
203         if (scsiDev.lun) // We only support lun 0\r
204         {\r
205                 scsiDev.data[0] = 0x7F;\r
206         }\r
207 }\r
208 \r
209 #pragma GCC pop_options\r