1 // Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
\r
2 // Copyright (C) 2014 Doug Brown <doug@downtowndougbrown.com>
\r
4 // This file is part of SCSI2SD.
\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
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
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
26 static const uint8 ReadWriteErrorRecoveryPage[] =
\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
36 0x00, // Don't try recovery algorithm during writes
\r
38 0x00, 0x00 // Recovery time limit 0 (use default)*/
\r
41 static const uint8 DisconnectReconnectPage[] =
\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
55 static const uint8 FormatDevicePage[] =
\r
57 0x03 | 0x80, // Page code | PS (persist) bit.
\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 0xFF, 0xFF, // Data bytes per physical sector. Configurable.
\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
72 static const uint8 RigidDiskDriveGeometry[] =
\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
83 0x00, // Rotational offset
\r
85 5400 >> 8, 5400 & 0xFF, // Medium rotation rate (RPM)
\r
86 0x00, 0x00 // Reserved
\r
89 static const uint8 CachingPage[] =
\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
101 static const uint8 ControlModePage[] =
\r
104 0x06, // Page length
\r
105 0x00, // No logging
\r
106 0x01, // Disable tagged queuing
\r
107 0x00, // No async event notifications
\r
109 0x00, 0x00 // AEN holdoff period.
\r
112 // Allow Apple 68k Drive Setup to format this drive.
\r
114 static const uint8 AppleVendorPage[] =
\r
118 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
\r
119 'A','P','P','L','E',' ','C','O','M','P','U','T','E','R',',',' ','I','N','C','.'
\r
122 static void pageIn(int pc, int dataIdx, const uint8* pageData, int pageLen)
\r
124 memcpy(&scsiDev.data[dataIdx], pageData, pageLen);
\r
126 if (pc == 0x01) // Mask out (un)changable values
\r
128 memset(&scsiDev.data[dataIdx+2], 0, pageLen - 2);
\r
132 static void doModeSense(
\r
133 int sixByteCmd, int dbd, int pc, int pageCode, int allocLength)
\r
135 if (pc == 0x03) // Saved Values not supported.
\r
137 scsiDev.status = CHECK_CONDITION;
\r
138 scsiDev.sense.code = ILLEGAL_REQUEST;
\r
139 scsiDev.sense.asc = SAVING_PARAMETERS_NOT_SUPPORTED;
\r
140 scsiDev.phase = STATUS;
\r
146 ////////////// Mode Parameter Header
\r
147 ////////////////////////////////////
\r
149 // Skip the Mode Data Length, we set that last.
\r
151 if (!sixByteCmd) ++idx;
\r
153 scsiDev.data[idx++] = 0; // Medium type. 0 = default
\r
155 // Device-specific parameter. Contains cache bits (0) and
\r
156 // a Write-Protect bit.
\r
157 scsiDev.data[idx++] = (blockDev.state & DISK_WP) ? 0x80 : 0;
\r
163 scsiDev.data[idx++] = 0; // No block descriptor
\r
167 // One block descriptor of length 8 bytes.
\r
168 scsiDev.data[idx++] = 8;
\r
173 scsiDev.data[idx++] = 0; // Reserved
\r
174 scsiDev.data[idx++] = 0; // Reserved
\r
177 scsiDev.data[idx++] = 0; // No block descriptor
\r
178 scsiDev.data[idx++] = 0; // No block descriptor
\r
182 // One block descriptor of length 8 bytes.
\r
183 scsiDev.data[idx++] = 0;
\r
184 scsiDev.data[idx++] = 8;
\r
188 ////////////// Block Descriptor
\r
189 ////////////////////////////////////
\r
192 scsiDev.data[idx++] = 0; // Density code. Reserved for direct-access
\r
193 // Number of blocks
\r
194 // Zero == all remaining blocks shall have the medium
\r
195 // characteristics specified.
\r
196 scsiDev.data[idx++] = 0;
\r
197 scsiDev.data[idx++] = 0;
\r
198 scsiDev.data[idx++] = 0;
\r
200 scsiDev.data[idx++] = 0; // reserved
\r
203 scsiDev.data[idx++] = config->bytesPerSector >> 16;
\r
204 scsiDev.data[idx++] = config->bytesPerSector >> 8;
\r
205 scsiDev.data[idx++] = config->bytesPerSector & 0xFF;
\r
214 pageIn(pc, idx, ReadWriteErrorRecoveryPage, sizeof(ReadWriteErrorRecoveryPage));
\r
215 idx += sizeof(ReadWriteErrorRecoveryPage);
\r
216 if (pageCode != 0x3f) break;
\r
219 pageIn(pc, idx, DisconnectReconnectPage, sizeof(DisconnectReconnectPage));
\r
220 idx += sizeof(DisconnectReconnectPage);
\r
221 if (pageCode != 0x3f) break;
\r
224 pageIn(pc, idx, FormatDevicePage, sizeof(FormatDevicePage));
\r
227 // Fill out the configured bytes-per-sector
\r
228 scsiDev.data[idx+12] = config->bytesPerSector >> 8;
\r
229 scsiDev.data[idx+13] = config->bytesPerSector & 0xFF;
\r
233 // Set a mask for the changeable values.
\r
234 scsiDev.data[idx+12] = 0xFF;
\r
235 scsiDev.data[idx+13] = 0xFF;
\r
238 idx += sizeof(FormatDevicePage);
\r
239 if (pageCode != 0x3f) break;
\r
243 pageIn(pc, idx, RigidDiskDriveGeometry, sizeof(RigidDiskDriveGeometry));
\r
247 // Need to fill out the number of cylinders.
\r
251 LBA2CHS(getScsiCapacity(), &cyl, &head, §or);
\r
253 scsiDev.data[idx+2] = cyl >> 16;
\r
254 scsiDev.data[idx+3] = cyl >> 8;
\r
255 scsiDev.data[idx+4] = cyl;
\r
257 memcpy(&scsiDev.data[idx+6], &scsiDev.data[idx+2], 3);
\r
258 memcpy(&scsiDev.data[idx+9], &scsiDev.data[idx+2], 3);
\r
261 idx += sizeof(RigidDiskDriveGeometry);
\r
262 if (pageCode != 0x3f) break;
\r
266 pageIn(pc, idx, CachingPage, sizeof(CachingPage));
\r
267 idx += sizeof(CachingPage);
\r
268 if (pageCode != 0x3f) break;
\r
271 pageIn(pc, idx, ControlModePage, sizeof(ControlModePage));
\r
272 idx += sizeof(ControlModePage);
\r
276 pageIn(pc, idx, AppleVendorPage, sizeof(AppleVendorPage));
\r
277 idx += sizeof(AppleVendorPage);
\r
281 // Unknown Page Code
\r
283 scsiDev.status = CHECK_CONDITION;
\r
284 scsiDev.sense.code = ILLEGAL_REQUEST;
\r
285 scsiDev.sense.asc = INVALID_FIELD_IN_CDB;
\r
286 scsiDev.phase = STATUS;
\r
290 if (idx > allocLength)
\r
292 // Chop the reply off early if shorter length is requested
\r
298 // Go back and fill out the mode data length
\r
301 // Cannot currently exceed limits. yay
\r
302 scsiDev.data[0] = idx - 1;
\r
306 scsiDev.data[0] = ((idx - 2) >> 8);
\r
307 scsiDev.data[1] = (idx - 2);
\r
310 scsiDev.dataLen = idx;
\r
311 scsiDev.phase = DATA_IN;
\r
316 scsiDev.status = CHECK_CONDITION;
\r
317 scsiDev.sense.code = ILLEGAL_REQUEST;
\r
318 scsiDev.sense.asc = INVALID_FIELD_IN_CDB;
\r
319 scsiDev.phase = STATUS;
\r
324 // Callback after the DATA OUT phase is complete.
\r
325 static void doModeSelect(void)
\r
327 if (scsiDev.status == GOOD) // skip if we've already encountered an error
\r
329 // scsiDev.dataLen bytes are in scsiDev.data
\r
332 if (scsiDev.cdb[0] == 0x15)
\r
335 (((uint16_t)scsiDev.data[6]) << 8) |scsiDev.data[7];
\r
336 idx = 8 + blockDescLen;
\r
340 int blockDescLen = scsiDev.data[3];
\r
341 idx = 4 + blockDescLen;
\r
343 if (idx > scsiDev.dataLen) goto bad;
\r
345 while (idx < scsiDev.dataLen)
\r
347 int pageLen = scsiDev.data[idx + 1];
\r
348 if (idx + 2 + pageLen > scsiDev.dataLen) goto bad;
\r
350 int pageCode = scsiDev.data[idx] & 0x3F;
\r
353 case 0x03: // Format Device Page
\r
355 if (pageLen != 0x16) goto bad;
\r
357 // Fill out the configured bytes-per-sector
\r
358 uint16_t bytesPerSector =
\r
359 (((uint16_t)scsiDev.data[idx+12]) << 8) |
\r
360 scsiDev.data[idx+13];
\r
362 // Sane values only, ok ?
\r
363 if ((bytesPerSector < MIN_SECTOR_SIZE) ||
\r
364 (bytesPerSector > MAX_SECTOR_SIZE))
\r
369 config->bytesPerSector = bytesPerSector;
\r
370 if (scsiDev.cdb[1] & 1) // SP Save Pages flag
\r
384 scsiDev.status = CHECK_CONDITION;
\r
385 scsiDev.sense.code = ILLEGAL_REQUEST;
\r
386 scsiDev.sense.asc = INVALID_FIELD_IN_PARAMETER_LIST;
\r
389 scsiDev.phase = STATUS;
\r
392 int scsiModeCommand()
\r
394 int commandHandled = 1;
\r
396 uint8 command = scsiDev.cdb[0];
\r
398 // We don't currently support the setting of any parameters.
\r
399 // (ie. no MODE SELECT(6) or MODE SELECT(10) commands)
\r
401 if (command == 0x1A)
\r
404 int dbd = scsiDev.cdb[1] & 0x08; // Disable block descriptors
\r
405 int pc = scsiDev.cdb[2] >> 6; // Page Control
\r
406 int pageCode = scsiDev.cdb[2] & 0x3F;
\r
407 int allocLength = scsiDev.cdb[4];
\r
408 if (allocLength == 0) allocLength = 256;
\r
409 doModeSense(1, dbd, pc, pageCode, allocLength);
\r
411 else if (command == 0x5A)
\r
414 int dbd = scsiDev.cdb[1] & 0x08; // Disable block descriptors
\r
415 int pc = scsiDev.cdb[2] >> 6; // Page Control
\r
416 int pageCode = scsiDev.cdb[2] & 0x3F;
\r
418 (((uint16) scsiDev.cdb[7]) << 8) +
\r
420 doModeSense(0, dbd, pc, pageCode, allocLength);
\r
422 else if (command == 0x15)
\r
425 int len = scsiDev.cdb[4];
\r
428 // If len == 0, then transfer no data. From the SCSI 2 standard:
\r
429 // A parameter list length of zero indicates that no data shall
\r
430 // be transferred. This condition shall not be considered as an
\r
432 scsiDev.phase = STATUS;
\r
436 scsiDev.dataLen = len;
\r
437 scsiDev.phase = DATA_OUT;
\r
438 scsiDev.postDataOutHook = doModeSelect;
\r
441 else if (command == 0x55)
\r
444 int allocLength = (((uint16) scsiDev.cdb[7]) << 8) + scsiDev.cdb[8];
\r
445 if (allocLength == 0)
\r
447 // If len == 0, then transfer no data. From the SCSI 2 standard:
\r
448 // A parameter list length of zero indicates that no data shall
\r
449 // be transferred. This condition shall not be considered as an
\r
451 scsiDev.phase = STATUS;
\r
455 scsiDev.dataLen = allocLength;
\r
456 scsiDev.phase = DATA_OUT;
\r
457 scsiDev.postDataOutHook = doModeSelect;
\r
462 commandHandled = 0;
\r
465 return commandHandled;
\r