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