Use the LUN provided in IDENTIFY messages in preference to the CDB.
[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 \r
18 #include "device.h"\r
19 #include "scsi.h"\r
20 #include "config.h"\r
21 #include "inquiry.h"\r
22 \r
23 #include <string.h>\r
24 \r
25 static uint8 StandardResponse[] =\r
26 {\r
27 0x00, // "Direct-access device". AKA standard hard disk\r
28 0x00, // device type qualifier\r
29 0x02, // Complies with ANSI SCSI-2.\r
30 0x01, // Response format is compatible with the old CCS format.\r
31 0x1f, // standard length.\r
32 0, 0, // Reserved\r
33 0x08 // Enable linked commands\r
34 };\r
35 // Vendor set by config 'c','o','d','e','s','r','c',' ',\r
36 // prodId set by config'S','C','S','I','2','S','D',' ',' ',' ',' ',' ',' ',' ',' ',' ',\r
37 // Revision set by config'2','.','0','a'\r
38 \r
39 /* For reference, here's a dump from an Apple branded 500Mb drive from 1994.\r
40 $ sudo sg_inq -H /dev/sdd --len 255\r
41 standard INQUIRY:\r
42  00     00 00 02 01 31 00 00 18  51 55 41 4e 54 55 4d 20    ....1...QUANTUM \r
43  10     4c 50 53 32 37 30 20 20  20 20 20 20 20 20 20 20    LPS270          \r
44  20     30 39 30 30 00 00 00 d9  b0 27 34 01 04 b3 01 1b    0900.....'4.....\r
45  30     07 00 a0 00 00 ff                                   ......\r
46  Vendor identification: QUANTUM \r
47  Product identification: LPS270          \r
48  Product revision level: 0900\r
49 */\r
50 \r
51 \r
52 static const uint8 SupportedVitalPages[] =\r
53 {\r
54 0x00, // "Direct-access device". AKA standard hard disk\r
55 0x00, // Page Code\r
56 0x00, // Reserved\r
57 0x04, // Page length\r
58 0x00, // Support "Supported vital product data pages"\r
59 0x80, // Support "Unit serial number page"\r
60 0x81, // Support "Implemented operating definition page"\r
61 0x82 // Support "ASCII Implemented operating definition page"\r
62 };\r
63 \r
64 static const uint8 UnitSerialNumber[] =\r
65 {\r
66 0x00, // "Direct-access device". AKA standard hard disk\r
67 0x80, // Page Code\r
68 0x00, // Reserved\r
69 0x10, // Page length\r
70 'c','o','d','e','s','r','c','-','1','2','3','4','5','6','7','8'\r
71 };\r
72 \r
73 static const uint8 ImpOperatingDefinition[] =\r
74 {\r
75 0x00, // "Direct-access device". AKA standard hard disk\r
76 0x81, // Page Code\r
77 0x00, // Reserved\r
78 0x03, // Page length\r
79 0x03, // Current: SCSI-2 operating definition\r
80 0x03, // Default: SCSI-2 operating definition\r
81 0x03 // Supported (list): SCSI-2 operating definition.\r
82 };\r
83 \r
84 static const uint8 AscImpOperatingDefinition[] =\r
85 {\r
86 0x00, // "Direct-access device". AKA standard hard disk\r
87 0x82, // Page Code\r
88 0x00, // Reserved\r
89 0x07, // Page length\r
90 0x06, // Ascii length\r
91 'S','C','S','I','-','2'\r
92 };\r
93 \r
94 void scsiInquiry()\r
95 {\r
96         uint8 evpd = scsiDev.cdb[1] & 1; // enable vital product data.\r
97         uint8 pageCode = scsiDev.cdb[2];\r
98         uint32 allocationLength = scsiDev.cdb[4];\r
99         if (allocationLength == 0) allocationLength = 256;\r
100         \r
101         if (!evpd)\r
102         {\r
103                 if (pageCode)\r
104                 {\r
105                         // error.\r
106                         scsiDev.status = CHECK_CONDITION;\r
107                         scsiDev.sense.code = ILLEGAL_REQUEST;\r
108                         scsiDev.sense.asc = INVALID_FIELD_IN_CDB;\r
109                         scsiDev.phase = STATUS;\r
110                 }\r
111                 else\r
112                 {\r
113                         memcpy(scsiDev.data, StandardResponse, sizeof(StandardResponse));\r
114                         memcpy(&scsiDev.data[8], config->vendor, sizeof(config->vendor));\r
115                         memcpy(&scsiDev.data[16], config->prodId, sizeof(config->prodId));\r
116                         memcpy(&scsiDev.data[32], config->revision, sizeof(config->revision));\r
117                         scsiDev.dataLen = sizeof(StandardResponse) +\r
118                                 sizeof(config->vendor) +\r
119                                 sizeof(config->prodId) +\r
120                                 sizeof(config->revision);\r
121                         scsiDev.phase = DATA_IN;\r
122                 }\r
123         }\r
124         else if (pageCode == 0x00)\r
125         {\r
126                 memcpy(scsiDev.data, SupportedVitalPages, sizeof(SupportedVitalPages));\r
127                 scsiDev.dataLen = sizeof(SupportedVitalPages);\r
128                 scsiDev.phase = DATA_IN;\r
129         }\r
130         else if (pageCode == 0x80)\r
131         {\r
132                 memcpy(scsiDev.data, UnitSerialNumber, sizeof(UnitSerialNumber));\r
133                 scsiDev.dataLen = sizeof(UnitSerialNumber);\r
134                 scsiDev.phase = DATA_IN;\r
135         }\r
136         else if (pageCode == 0x81)\r
137         {\r
138                 memcpy(\r
139                         scsiDev.data,\r
140                         ImpOperatingDefinition,\r
141                         sizeof(ImpOperatingDefinition));\r
142                 scsiDev.dataLen = sizeof(ImpOperatingDefinition);\r
143                 scsiDev.phase = DATA_IN;\r
144         }\r
145         else if (pageCode == 0x82)\r
146         {\r
147                 memcpy(\r
148                         scsiDev.data,\r
149                         AscImpOperatingDefinition,\r
150                         sizeof(AscImpOperatingDefinition));\r
151                 scsiDev.dataLen = sizeof(AscImpOperatingDefinition);\r
152                 scsiDev.phase = DATA_IN;\r
153         }\r
154         else\r
155         {\r
156                 // error.\r
157                 scsiDev.status = CHECK_CONDITION;\r
158                 scsiDev.sense.code = ILLEGAL_REQUEST;\r
159                 scsiDev.sense.asc = INVALID_FIELD_IN_CDB;\r
160                 scsiDev.phase = STATUS;\r
161         }\r
162 \r
163 \r
164         if (scsiDev.phase == DATA_IN)\r
165         {\r
166                 // "real" hard drives send back exactly allocationLenth bytes, padded\r
167                 // with zeroes. This only seems to happen for Inquiry responses, and not\r
168                 // other commands that also supply an allocation length such as Mode Sense or\r
169                 // Request Sense.\r
170                 if (scsiDev.dataLen < allocationLength)\r
171                 {\r
172                         memset(\r
173                                 &scsiDev.data[scsiDev.dataLen],\r
174                                 0,\r
175                                 allocationLength - scsiDev.dataLen);\r
176                 }\r
177                 // Spec 8.2.5 requires us to simply truncate the response if it's too big.\r
178                 scsiDev.dataLen = allocationLength;\r
179         }\r
180 \r
181         // Set the first byte to indicate LUN presence.\r
182         if (scsiDev.lun) // We only support lun 0\r
183         {\r
184                 scsiDev.data[0] = 0x7F;\r
185         }\r
186 }\r
187 \r