Merge Powerbook PCB updates
[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         uint8 lun = scsiDev.cdb[1] >> 5;\r
99         uint32 allocationLength = scsiDev.cdb[4];\r
100         if (allocationLength == 0) allocationLength = 256;\r
101         \r
102         if (!evpd)\r
103         {\r
104                 if (pageCode)\r
105                 {\r
106                         // error.\r
107                         scsiDev.status = CHECK_CONDITION;\r
108                         scsiDev.sense.code = ILLEGAL_REQUEST;\r
109                         scsiDev.sense.asc = INVALID_FIELD_IN_CDB;\r
110                         scsiDev.phase = STATUS;\r
111                 }\r
112                 else\r
113                 {\r
114                         memcpy(scsiDev.data, StandardResponse, sizeof(StandardResponse));\r
115                         memcpy(&scsiDev.data[8], config->vendor, sizeof(config->vendor));\r
116                         memcpy(&scsiDev.data[16], config->prodId, sizeof(config->prodId));\r
117                         memcpy(&scsiDev.data[32], config->revision, sizeof(config->revision));\r
118                         scsiDev.dataLen = sizeof(StandardResponse) +\r
119                                 sizeof(config->vendor) +\r
120                                 sizeof(config->prodId) +\r
121                                 sizeof(config->revision);\r
122                         scsiDev.phase = DATA_IN;\r
123                 }\r
124         }\r
125         else if (pageCode == 0x00)\r
126         {\r
127                 memcpy(scsiDev.data, SupportedVitalPages, sizeof(SupportedVitalPages));\r
128                 scsiDev.dataLen = sizeof(SupportedVitalPages);\r
129                 scsiDev.phase = DATA_IN;\r
130         }\r
131         else if (pageCode == 0x80)\r
132         {\r
133                 memcpy(scsiDev.data, UnitSerialNumber, sizeof(UnitSerialNumber));\r
134                 scsiDev.dataLen = sizeof(UnitSerialNumber);\r
135                 scsiDev.phase = DATA_IN;\r
136         }\r
137         else if (pageCode == 0x81)\r
138         {\r
139                 memcpy(\r
140                         scsiDev.data,\r
141                         ImpOperatingDefinition,\r
142                         sizeof(ImpOperatingDefinition));\r
143                 scsiDev.dataLen = sizeof(ImpOperatingDefinition);\r
144                 scsiDev.phase = DATA_IN;\r
145         }\r
146         else if (pageCode == 0x82)\r
147         {\r
148                 memcpy(\r
149                         scsiDev.data,\r
150                         AscImpOperatingDefinition,\r
151                         sizeof(AscImpOperatingDefinition));\r
152                 scsiDev.dataLen = sizeof(AscImpOperatingDefinition);\r
153                 scsiDev.phase = DATA_IN;\r
154         }\r
155         else\r
156         {\r
157                 // error.\r
158                 scsiDev.status = CHECK_CONDITION;\r
159                 scsiDev.sense.code = ILLEGAL_REQUEST;\r
160                 scsiDev.sense.asc = INVALID_FIELD_IN_CDB;\r
161                 scsiDev.phase = STATUS;\r
162         }\r
163 \r
164 \r
165         if (scsiDev.phase == DATA_IN)\r
166         {\r
167                 // "real" hard drives send back exactly allocationLenth bytes, padded\r
168                 // with zeroes. This only seems to happen for Inquiry responses, and not\r
169                 // other commands that also supply an allocation length such as Mode Sense or\r
170                 // Request Sense.\r
171                 if (scsiDev.dataLen < allocationLength)\r
172                 {\r
173                         memset(\r
174                                 &scsiDev.data[scsiDev.dataLen],\r
175                                 0,\r
176                                 allocationLength - scsiDev.dataLen);\r
177                 }\r
178                 // Spec 8.2.5 requires us to simply truncate the response if it's too big.\r
179                 scsiDev.dataLen = allocationLength;\r
180         }\r
181 \r
182         // Set the first byte to indicate LUN presence.\r
183         if (lun) // We only support lun 0\r
184         {\r
185                 scsiDev.data[0] = 0x7F;\r
186         }\r
187 }\r
188 \r