99e89b11e32f0bb48ef450c57ca09d8d0b3dfc14
[SCSI2SD.git] / software / SCSI2SD / SCSI2SD.cydsn / mode.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 "mode.h"\r
21 #include "disk.h"\r
22 \r
23 #include <string.h>\r
24 \r
25 static const uint8 DisconnectReconnectPage[] =\r
26 {\r
27 0x02, // Page code\r
28 0x0E, // Page length\r
29 0, // Buffer full ratio\r
30 0, // Buffer empty ratio\r
31 0x00, 10, // Bus inactivity limit, 100us increments. Allow 1ms.\r
32 0x00, 0x00, // Disconnect time limit\r
33 0x00, 0x00, // Connect time limit\r
34 0x00, 0x00, // Maximum burst size\r
35 0x00 ,// DTDC. Not used.\r
36 0x00, 0x00, 0x00 // Reserved\r
37 };\r
38 \r
39 static const uint8 FormatDevicePage[] =\r
40 {\r
41 0x03, // Page code \r
42 0x16, // Page length\r
43 0x00, 0x00, // Single zone\r
44 0x00, 0x00, // No alternate sectors\r
45 0x00, 0x00, // No alternate tracks\r
46 0x00, 0x00, // No alternate tracks per lun\r
47 0x00, SCSI_SECTORS_PER_TRACK, // Sectors per track\r
48 SCSI_SECTOR_SIZE >> 8, SCSI_SECTOR_SIZE & 0xFF, // Data bytes per physical sector\r
49 0x00, 0x01, // Interleave\r
50 0x00, 0x00, // Track skew factor\r
51 0x00, 0x00, // Cylinder skew factor\r
52 0xC0, // SSEC(set) HSEC(set) RMB SURF\r
53 0x00, 0x00, 0x00 // Reserved\r
54 };\r
55 \r
56 static const uint8 RigidDiskDriveGeometry[] =\r
57 {\r
58 0x04, // Page code\r
59 0x16, // Page length\r
60 0xFF, 0xFF, 0xFF, // Number of cylinders\r
61 SCSI_HEADS_PER_CYLINDER, // Number of heads\r
62 0xFF, 0xFF, 0xFF, // Starting cylinder-write precompensation\r
63 0xFF, 0xFF, 0xFF, // Starting cylinder-reduced write current\r
64 0x00, 0x1, // Drive step rate (units of 100ns)\r
65 0x00, 0x00, 0x00, // Landing zone cylinder\r
66 0x00, // RPL\r
67 0x00, // Rotational offset\r
68 0x00, // Reserved\r
69 5400 >> 8, 5400 & 0xFF, // Medium rotation rate (RPM)\r
70 0x00, 0x00 // Reserved\r
71 };\r
72 \r
73 static const uint8 CachingPage[] =\r
74 {\r
75 0x08, // Page Code\r
76 0x0A, // Page length\r
77 0x01, // Read cache disable\r
78 0x00, // No useful rention policy.\r
79 0x00, 0x00, // Pre-fetch always disabled\r
80 0x00, 0x00, // Minimum pre-fetch\r
81 0x00, 0x00, // Maximum pre-fetch\r
82 0x00, 0x00, // Maximum pre-fetch ceiling\r
83 };\r
84 \r
85 static const uint8 ControlModePage[] =\r
86 {\r
87 0x0A, // Page code\r
88 0x06, // Page length\r
89 0x00, // No logging\r
90 0x01, // Disable tagged queuing\r
91 0x00, // No async event notifications\r
92 0x00, // Reserved\r
93 0x00, 0x00 // AEN holdoff period.\r
94 };\r
95 \r
96 // Allow Apple 68k Drive Setup to format this drive.\r
97 // Code\r
98 // TODO make this string configurable.\r
99 static const uint8 AppleVendorPage[] =\r
100 {\r
101 0x30, // Page code\r
102 28, // Page length\r
103 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
104 'A','P','P','L','E',' ','C','O','M','P','U','T','E','R',',',' ','I','N','C','.'\r
105 };\r
106 \r
107 static void pageIn(int pc, int dataIdx, const uint8* pageData, int pageLen)\r
108 {\r
109         memcpy(&scsiDev.data[dataIdx], pageData, pageLen);\r
110 \r
111         if (pc == 0x01) // Mask out (un)changable values\r
112         {\r
113                 memset(&scsiDev.data[dataIdx+2], 0, pageLen - 2);\r
114         }\r
115 }\r
116 \r
117 static void doModeSense(\r
118         int sixByteCmd, int dbd, int pc, int pageCode, int allocLength)\r
119 {\r
120         // TODO Apple HD SC Drive Setup requests Page 3 (FormatDevicePage) with an\r
121         // allocLength of 0x20. We need 0x24 if we include a block descriptor, and\r
122         // thus return CHECK CONDITION. A block descriptor is optional, so we\r
123         // chose to ignore it.\r
124         // TODO make configurable\r
125         dbd = 1;\r
126         \r
127         if (pc == 0x03) // Saved Values not supported.\r
128         {\r
129                 scsiDev.status = CHECK_CONDITION;\r
130                 scsiDev.sense.code = ILLEGAL_REQUEST;\r
131                 scsiDev.sense.asc = SAVING_PARAMETERS_NOT_SUPPORTED;\r
132                 scsiDev.phase = STATUS;\r
133         }\r
134         else\r
135         {\r
136                 int pageFound = 1;\r
137                 \r
138                 ////////////// Mode Parameter Header\r
139                 ////////////////////////////////////\r
140 \r
141                 // Skip the Mode Data Length, we set that last.\r
142                 int idx = 1;\r
143                 if (!sixByteCmd) ++idx;\r
144 \r
145                 scsiDev.data[idx++] = 0; // Medium type. 0 = default\r
146 \r
147                 // Device-specific parameter. Contains cache bits (0) and\r
148                 // a Write-Protect bit.\r
149                 scsiDev.data[idx++] = (blockDev.state & DISK_WP) ? 0x80 : 0;\r
150 \r
151                 if (sixByteCmd)\r
152                 {\r
153                         if (dbd)\r
154                         {\r
155                                 scsiDev.data[idx++] = 0; // No block descriptor\r
156                         }\r
157                         else\r
158                         {\r
159                                 // One block descriptor of length 8 bytes.\r
160                                 scsiDev.data[idx++] = 8;\r
161                         }\r
162                 }\r
163                 else\r
164                 {\r
165                         scsiDev.data[idx++] = 0; // Reserved\r
166                         scsiDev.data[idx++] = 0; // Reserved\r
167                         if (dbd)\r
168                         {\r
169                                 scsiDev.data[idx++] = 0; // No block descriptor\r
170                                 scsiDev.data[idx++] = 0; // No block descriptor\r
171                         }\r
172                         else\r
173                         {\r
174                                 // One block descriptor of length 8 bytes.\r
175                                 scsiDev.data[idx++] = 0;\r
176                                 scsiDev.data[idx++] = 8;\r
177                         }\r
178                 }\r
179 \r
180                 ////////////// Block Descriptor\r
181                 ////////////////////////////////////\r
182                 if (!dbd)\r
183                 {\r
184                         scsiDev.data[idx++] = 0; // Density code. Reserved for direct-access\r
185                         // Number of blocks\r
186                         // Zero == all remaining blocks shall have the medium\r
187                         // characteristics specified.\r
188                         scsiDev.data[idx++] = 0;\r
189                         scsiDev.data[idx++] = 0;\r
190                         scsiDev.data[idx++] = 0;\r
191 \r
192                         scsiDev.data[idx++] = 0; // reserved\r
193 \r
194                         // Block length\r
195                         scsiDev.data[idx++] = SCSI_BLOCK_SIZE >> 16;\r
196                         scsiDev.data[idx++] = SCSI_BLOCK_SIZE >> 8;\r
197                         scsiDev.data[idx++] = SCSI_BLOCK_SIZE & 0xFF;\r
198                 }\r
199 \r
200                 switch (pageCode)\r
201                 {\r
202                 case 0x3F:\r
203                         // EVERYTHING\r
204 \r
205                 case 0x02:\r
206                         pageIn(pc, idx, DisconnectReconnectPage, sizeof(DisconnectReconnectPage));\r
207                         idx += sizeof(DisconnectReconnectPage);\r
208                         if (pageCode != 0x3f) break;\r
209 \r
210                 case 0x03:\r
211                         pageIn(pc, idx, FormatDevicePage, sizeof(FormatDevicePage));\r
212                         idx += sizeof(FormatDevicePage);\r
213                         if (pageCode != 0x3f) break;\r
214 \r
215                 case 0x04:\r
216                 {\r
217                         pageIn(pc, idx, RigidDiskDriveGeometry, sizeof(RigidDiskDriveGeometry));\r
218 \r
219                         if (pc != 0x01)\r
220                         {\r
221                                 // Need to fill out the number of cylinders.\r
222                                 uint32 cyl;\r
223                                 uint8 head;\r
224                                 uint32 sector;\r
225                                 LBA2CHS(blockDev.capacity, &cyl, &head, &sector);\r
226 \r
227                                 scsiDev.data[idx+2] = cyl >> 16;\r
228                                 scsiDev.data[idx+3] = cyl >> 8;\r
229                                 scsiDev.data[idx+4] = cyl;\r
230 \r
231                                 memcpy(&scsiDev.data[idx+6], &scsiDev.data[idx+2], 3);\r
232                                 memcpy(&scsiDev.data[idx+9], &scsiDev.data[idx+2], 3);\r
233                         }\r
234 \r
235                         idx += sizeof(RigidDiskDriveGeometry);\r
236                         if (pageCode != 0x3f) break;\r
237                 }\r
238 \r
239                 case 0x08:\r
240                         pageIn(pc, idx, CachingPage, sizeof(CachingPage));\r
241                         idx += sizeof(CachingPage);\r
242                         if (pageCode != 0x3f) break;\r
243 \r
244                 case 0x0A:\r
245                         pageIn(pc, idx, ControlModePage, sizeof(ControlModePage));\r
246                         idx += sizeof(ControlModePage);\r
247                         break;\r
248 \r
249                 case 0x30:\r
250                         pageIn(pc, idx, AppleVendorPage, sizeof(AppleVendorPage));\r
251                         idx += sizeof(AppleVendorPage);\r
252                         break;\r
253                         \r
254                 default:\r
255                         // Unknown Page Code\r
256                         pageFound = 0;\r
257                         scsiDev.status = CHECK_CONDITION;\r
258                         scsiDev.sense.code = ILLEGAL_REQUEST;\r
259                         scsiDev.sense.asc = INVALID_FIELD_IN_CDB;\r
260                         scsiDev.phase = STATUS;\r
261                 }\r
262 \r
263 \r
264                 if (idx > allocLength)\r
265                 {\r
266                         // Initiator may not have space to receive results.\r
267                         scsiDev.status = CHECK_CONDITION;\r
268                         scsiDev.sense.code = ILLEGAL_REQUEST;\r
269                         scsiDev.sense.asc = INVALID_FIELD_IN_CDB;\r
270                         scsiDev.phase = STATUS;\r
271                 }\r
272                 else if (pageFound)\r
273                 {\r
274                         // Go back and fill out the mode data length\r
275                         if (sixByteCmd)\r
276                         {\r
277                                 // Cannot currently exceed limits. yay\r
278                                 scsiDev.data[0] = idx - 1;\r
279                         }\r
280                         else\r
281                         {\r
282                                 scsiDev.data[0] = ((idx - 2) >> 8);\r
283                                 scsiDev.data[1] = (idx - 2);\r
284                         }\r
285 \r
286                         scsiDev.dataLen = idx;\r
287                         scsiDev.phase = DATA_IN;\r
288                 }\r
289                 else\r
290                 {\r
291                         // Initiator may not have space to receive results.\r
292                         scsiDev.status = CHECK_CONDITION;\r
293                         scsiDev.sense.code = ILLEGAL_REQUEST;\r
294                         scsiDev.sense.asc = INVALID_FIELD_IN_CDB;\r
295                         scsiDev.phase = STATUS;\r
296                 }\r
297         }\r
298 }\r
299 \r
300 int scsiModeCommand()\r
301 {\r
302         int commandHandled = 1;\r
303 \r
304         uint8 command = scsiDev.cdb[0];\r
305 \r
306         // We don't currently support the setting of any parameters.\r
307         // (ie. no MODE SELECT(6) or MODE SELECT(10) commands)\r
308 \r
309         if (command == 0x1A)\r
310         {\r
311                 // MODE SENSE(6)\r
312                 int dbd = scsiDev.cdb[1] & 0x08; // Disable block descriptors\r
313                 int pc = scsiDev.cdb[2] >> 6; // Page Control\r
314                 int pageCode = scsiDev.cdb[2] & 0x3F;\r
315                 int allocLength = scsiDev.cdb[4];\r
316                 if (allocLength == 0) allocLength = 256;\r
317                 doModeSense(1, dbd, pc, pageCode, allocLength);\r
318         }\r
319         else if (command == 0x5A)\r
320         {\r
321                 // MODE SENSE(10)\r
322                 int dbd = scsiDev.cdb[1] & 0x08; // Disable block descriptors\r
323                 int pc = scsiDev.cdb[2] >> 6; // Page Control\r
324                 int pageCode = scsiDev.cdb[2] & 0x3F;\r
325                 int allocLength =\r
326                         (((uint16) scsiDev.cdb[7]) << 8) +\r
327                         scsiDev.cdb[8];\r
328                 doModeSense(0, dbd, pc, pageCode, allocLength);\r
329         }\r
330         else if (command == 0x15)\r
331         {\r
332                 // MODE SELECT(6)\r
333                 int len = scsiDev.cdb[4];\r
334                 if (len == 0) len = 256;\r
335                 scsiDev.dataLen = len;\r
336                 scsiDev.phase = DATA_OUT;\r
337         }\r
338         else if (command == 0x55)\r
339         {\r
340                 // MODE SELECT(10)\r
341                 int allocLength = (((uint16) scsiDev.cdb[7]) << 8) + scsiDev.cdb[8];\r
342                 scsiDev.dataLen = allocLength;\r
343                 scsiDev.phase = DATA_OUT;\r
344         }\r
345         else\r
346         {\r
347                 commandHandled = 0;\r
348         }\r
349 \r
350         return commandHandled;\r
351 }\r
352 \r
353 \r