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