95d00e9ab587a5fa5c088cfdcf72a5c9b96f7430
[SCSI2SD.git] / software / SCSI2SD / src / 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 #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 ReadWriteErrorRecoveryPage[] =\r
26 {\r
27 0x01, // Page code\r
28 0x0A, // Page length\r
29 \r
30 // VMS 5.5-2 is very particular regarding the mode page values.\r
31 // The required values for a SCSI2/NoTCQ device are:\r
32 // AWRE=0 ARRE=0 TB=1 RC=0 EER=? PER=1 DTE=1 DCR=?\r
33 // See ftp://www.digiater.nl/openvms/decus/vms94b/net94b/scsi_params_dkdriver.txt\r
34 // X-Newsgroups: comp.os.vms\r
35 // Subject: Re: VMS 6.1 vs. Seagate Disk Drives\r
36 // Message-Id: <32g87h$8q@nntpd.lkg.dec.com>\r
37 // From: weber@evms.enet.dec.com (Ralph O. Weber -- OpenVMS AXP)\r
38 // Date: 12 Aug 1994 16:32:49 GMT\r
39 0x26,\r
40 \r
41 0x00, // Don't try recovery algorithm during reads\r
42 0x00, // Correction span 0\r
43 0x00, // Head offset count 0,\r
44 0x00, // Data strobe offset count 0,\r
45 0x00, // Reserved\r
46 0x00, // Don't try recovery algorithm during writes\r
47 0x00, // Reserved\r
48 0x00, 0x00 // Recovery time limit 0 (use default)*/\r
49 };\r
50 \r
51 static const uint8 ReadWriteErrorRecoveryPage_SCSI1[] =\r
52 {\r
53 0x01, // Page code\r
54 0x06, // Page length\r
55 0x26,\r
56 0x00, // Don't try recovery algorithm during reads\r
57 0x00, // Correction span 0\r
58 0x00, // Head offset count 0,\r
59 0x00, // Data strobe offset count 0,\r
60 0xFF // Reserved\r
61 };\r
62 \r
63 static const uint8 DisconnectReconnectPage[] =\r
64 {\r
65 0x02, // Page code\r
66 0x0E, // Page length\r
67 0, // Buffer full ratio\r
68 0, // Buffer empty ratio\r
69 0x00, 10, // Bus inactivity limit, 100us increments. Allow 1ms.\r
70 0x00, 0x00, // Disconnect time limit\r
71 0x00, 0x00, // Connect time limit\r
72 0x00, 0x00, // Maximum burst size\r
73 0x00 ,// DTDC. Not used.\r
74 0x00, 0x00, 0x00 // Reserved\r
75 };\r
76 \r
77 static const uint8 DisconnectReconnectPage_SCSI1[] =\r
78 {\r
79 0x02, // Page code\r
80 0x0A, // Page length\r
81 0, // Buffer full ratio\r
82 0, // Buffer empty ratio\r
83 0x00, 10, // Bus inactivity limit, 100us increments. Allow 1ms.\r
84 0x00, 0x00, // Disconnect time limit\r
85 0x00, 0x00, // Connect time limit\r
86 0x00, 0x00 // Maximum burst size\r
87 };\r
88 \r
89 static const uint8 FormatDevicePage[] =\r
90 {\r
91 0x03 | 0x80, // Page code | PS (persist) bit.\r
92 0x16, // Page length\r
93 0x00, 0x00, // Single zone\r
94 0x00, 0x00, // No alternate sectors\r
95 0x00, 0x00, // No alternate tracks\r
96 0x00, 0x00, // No alternate tracks per lun\r
97 0x00, 0x00, // Sectors per track, configurable\r
98 0xFF, 0xFF, // Data bytes per physical sector. Configurable.\r
99 0x00, 0x01, // Interleave\r
100 0x00, 0x00, // Track skew factor\r
101 0x00, 0x00, // Cylinder skew factor\r
102 0xC0, // SSEC(set) HSEC(set) RMB SURF\r
103 0x00, 0x00, 0x00 // Reserved\r
104 };\r
105 \r
106 static const uint8 RigidDiskDriveGeometry[] =\r
107 {\r
108 0x04, // Page code\r
109 0x16, // Page length\r
110 0xFF, 0xFF, 0xFF, // Number of cylinders\r
111 0x00, // Number of heads (replaced by configured value)\r
112 0xFF, 0xFF, 0xFF, // Starting cylinder-write precompensation\r
113 0xFF, 0xFF, 0xFF, // Starting cylinder-reduced write current\r
114 0x00, 0x1, // Drive step rate (units of 100ns)\r
115 0x00, 0x00, 0x00, // Landing zone cylinder\r
116 0x00, // RPL\r
117 0x00, // Rotational offset\r
118 0x00, // Reserved\r
119 5400 >> 8, 5400 & 0xFF, // Medium rotation rate (RPM)\r
120 0x00, 0x00 // Reserved\r
121 };\r
122 \r
123 static const uint8 FlexibleDiskDriveGeometry[] =\r
124 {\r
125 0x05, // Page code\r
126 0x1E, // Page length\r
127 0x01, 0xF4, // Transfer Rate (500kbits)\r
128 0x01, // heads\r
129 18, // sectors per track\r
130 0x20,0x00, // bytes per sector\r
131 0x00, 80, // Cylinders\r
132 0x00, 0x80, // Write-precomp\r
133 0x00, 0x80, // reduced current,\r
134 0x00, 0x00, // Drive step rate\r
135 0x00, // pulse width\r
136 0x00, 0x00, // Head settle delay\r
137 0x00, // motor on delay\r
138 0x00,  // motor off delay\r
139 0x00,\r
140 0x00,\r
141 0x00,\r
142 0x00,\r
143 0x00,\r
144 0x00,\r
145 0x00,\r
146 0x00,\r
147 0x00,\r
148 0x00,\r
149 0x00\r
150 };\r
151 \r
152 static const uint8 RigidDiskDriveGeometry_SCSI1[] =\r
153 {\r
154 0x04, // Page code\r
155 0x12, // Page length\r
156 0xFF, 0xFF, 0xFF, // Number of cylinders\r
157 0x00, // Number of heads (replaced by configured value)\r
158 0xFF, 0xFF, 0xFF, // Starting cylinder-write precompensation\r
159 0xFF, 0xFF, 0xFF, // Starting cylinder-reduced write current\r
160 0x00, 0x1, // Drive step rate (units of 100ns)\r
161 0x00, 0x00, 0x00, // Landing zone cylinder\r
162 0x00, // RPL\r
163 0x00, // Rotational offset\r
164 0x00 // Reserved\r
165 };\r
166 \r
167 static const uint8 CachingPage[] =\r
168 {\r
169 0x08, // Page Code\r
170 0x0A, // Page length\r
171 0x01, // Read cache disable\r
172 0x00, // No useful rention policy.\r
173 0x00, 0x00, // Pre-fetch always disabled\r
174 0x00, 0x00, // Minimum pre-fetch\r
175 0x00, 0x00, // Maximum pre-fetch\r
176 0x00, 0x00, // Maximum pre-fetch ceiling\r
177 };\r
178 \r
179 static const uint8 ControlModePage[] =\r
180 {\r
181 0x0A, // Page code\r
182 0x06, // Page length\r
183 0x00, // No logging\r
184 0x01, // Disable tagged queuing\r
185 0x00, // No async event notifications\r
186 0x00, // Reserved\r
187 0x00, 0x00 // AEN holdoff period.\r
188 };\r
189 \r
190 static const uint8_t SequentialDeviceConfigPage[] =\r
191 {\r
192 0x10, // page code\r
193 0x0E, // Page length\r
194 0x00, // CAP, CAF, Active Format\r
195 0x00, // Active partition\r
196 0x00, // Write buffer full ratio\r
197 0x00, // Read buffer empty ratio\r
198 0x00,0x01, // Write delay time, in 100ms units\r
199 0x00, // Default gap size\r
200 0x10, // auto-generation of default eod (end of data)\r
201 0x00,0x00,0x00, // buffer-size at early warning\r
202 0x00, // No data compression\r
203 0x00 // reserved\r
204 };\r
205 \r
206 // Allow Apple 68k Drive Setup to format this drive.\r
207 // Code\r
208 static const uint8 AppleVendorPage[] =\r
209 {\r
210 0x30, // Page code\r
211 28, // Page length\r
212 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\r
213 'A','P','P','L','E',' ','C','O','M','P','U','T','E','R',',',' ','I','N','C','.'\r
214 };\r
215 \r
216 static void pageIn(int pc, int dataIdx, const uint8* pageData, int pageLen)\r
217 {\r
218         memcpy(&scsiDev.data[dataIdx], pageData, pageLen);\r
219 \r
220         if (pc == 0x01) // Mask out (un)changable values\r
221         {\r
222                 memset(&scsiDev.data[dataIdx+2], 0, pageLen - 2);\r
223         }\r
224 }\r
225 \r
226 static int useCustomPages(const TargetConfig* cfg, int pc, int pageCode, int* idx)\r
227 {\r
228         int found = 0;\r
229         int cfgIdx = 0;\r
230         while ((cfgIdx < sizeof(cfg->modePages) - 2) &&\r
231                 (cfg->modePages[cfgIdx + 1] != 0)\r
232                 )\r
233         {\r
234                 int pageSize = cfg->modePages[cfgIdx + 1] + 2;\r
235                 int dataPageCode = cfg->modePages[cfgIdx] & 0x3f;\r
236                 if ((dataPageCode == pageCode) ||\r
237                         (pageCode == 0x3f)\r
238                         )\r
239                 {\r
240                         pageIn(pc, *idx, &cfg->modePages[cfgIdx], pageSize);\r
241                         *idx += pageSize;\r
242                         found = 1;\r
243                         if (pageCode != 0x3f)\r
244                         {\r
245                                 break;\r
246                         }\r
247                 }\r
248                 cfgIdx += pageSize;\r
249         }\r
250         return found;\r
251 }\r
252 \r
253 static void doModeSense(\r
254         int sixByteCmd, int dbd, int pc, int pageCode, int allocLength)\r
255 {\r
256         ////////////// Mode Parameter Header\r
257         ////////////////////////////////////\r
258 \r
259         // Skip the Mode Data Length, we set that last.\r
260         int idx = 1;\r
261         if (!sixByteCmd) ++idx;\r
262 \r
263         uint8_t mediumType = 0;\r
264         uint8_t deviceSpecificParam = 0;\r
265         uint8_t density = 0;\r
266         switch (scsiDev.target->cfg->deviceType)\r
267         {\r
268         case CONFIG_FIXED:\r
269         case CONFIG_REMOVEABLE:\r
270                 mediumType = 0; // We should support various floppy types here!\r
271                 // Contains cache bits (0) and a Write-Protect bit.\r
272                 deviceSpecificParam =\r
273                         (blockDev.state & DISK_WP) ? 0x80 : 0;\r
274                 density = 0; // reserved for direct access\r
275                 break;\r
276 \r
277         case CONFIG_FLOPPY_14MB:\r
278                 mediumType = 0x1E; // 90mm/3.5"\r
279                 deviceSpecificParam =\r
280                         (blockDev.state & DISK_WP) ? 0x80 : 0;\r
281                 density = 0; // reserved for direct access\r
282                 break;\r
283 \r
284         case CONFIG_OPTICAL:\r
285                 mediumType = 0x02; // 120mm CDROM, data only.\r
286                 deviceSpecificParam = 0;\r
287                 density = 0x01; // User data only, 2048bytes per sector.\r
288                 break;\r
289 \r
290         case CONFIG_SEQUENTIAL:\r
291                 mediumType = 0; // reserved\r
292                 deviceSpecificParam =\r
293                         (blockDev.state & DISK_WP) ? 0x80 : 0;\r
294                 density = 0x13; // DAT Data Storage, X3B5/88-185A \r
295                 break;\r
296 \r
297         case CONFIG_MO:\r
298         mediumType = 0x03; // Optical reversible or erasable medium\r
299                 deviceSpecificParam =\r
300                         (blockDev.state & DISK_WP) ? 0x80 : 0;\r
301                 density = 0x00; // Default\r
302                 break;\r
303 \r
304         };\r
305 \r
306         scsiDev.data[idx++] = mediumType;\r
307         scsiDev.data[idx++] = deviceSpecificParam;\r
308 \r
309         if (sixByteCmd)\r
310         {\r
311                 if (dbd)\r
312                 {\r
313                         scsiDev.data[idx++] = 0; // No block descriptor\r
314                 }\r
315                 else\r
316                 {\r
317                         // One block descriptor of length 8 bytes.\r
318                         scsiDev.data[idx++] = 8;\r
319                 }\r
320         }\r
321         else\r
322         {\r
323                 scsiDev.data[idx++] = 0; // Reserved\r
324                 scsiDev.data[idx++] = 0; // Reserved\r
325                 if (dbd)\r
326                 {\r
327                         scsiDev.data[idx++] = 0; // No block descriptor\r
328                         scsiDev.data[idx++] = 0; // No block descriptor\r
329                 }\r
330                 else\r
331                 {\r
332                         // One block descriptor of length 8 bytes.\r
333                         scsiDev.data[idx++] = 0;\r
334                         scsiDev.data[idx++] = 8;\r
335                 }\r
336         }\r
337 \r
338         ////////////// Block Descriptor\r
339         ////////////////////////////////////\r
340         if (!dbd)\r
341         {\r
342                 scsiDev.data[idx++] = density;\r
343                 // Number of blocks\r
344                 // Zero == all remaining blocks shall have the medium\r
345                 // characteristics specified.\r
346                 scsiDev.data[idx++] = 0;\r
347                 scsiDev.data[idx++] = 0;\r
348                 scsiDev.data[idx++] = 0;\r
349 \r
350                 scsiDev.data[idx++] = 0; // reserved\r
351 \r
352                 // Block length\r
353                 uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;\r
354                 scsiDev.data[idx++] = bytesPerSector >> 16;\r
355                 scsiDev.data[idx++] = bytesPerSector >> 8;\r
356                 scsiDev.data[idx++] = bytesPerSector & 0xFF;\r
357         }\r
358 \r
359         int pageFound = 0;\r
360 \r
361         if (scsiDev.target->cfg->modePages[1] != 0)\r
362         {\r
363                 pageFound = useCustomPages(scsiDev.target->cfg, pc, pageCode, &idx);\r
364                 if (pageFound && pageCode != 0x3F) pageCode = 0xFF; // skip rest of logic\r
365         }\r
366 \r
367         if (pageCode == 0x01 || pageCode == 0x3F)\r
368         {\r
369                 pageFound = 1;\r
370                 if ((scsiDev.compatMode >= COMPAT_SCSI2))\r
371                 {\r
372                         pageIn(pc, idx, ReadWriteErrorRecoveryPage, sizeof(ReadWriteErrorRecoveryPage));\r
373                         idx += sizeof(ReadWriteErrorRecoveryPage);\r
374                 }\r
375                 else\r
376                 {\r
377                         pageIn(pc, idx, ReadWriteErrorRecoveryPage_SCSI1, sizeof(ReadWriteErrorRecoveryPage_SCSI1));\r
378                         idx += sizeof(ReadWriteErrorRecoveryPage_SCSI1);\r
379                 }\r
380         }\r
381 \r
382         if (pageCode == 0x02 || pageCode == 0x3F)\r
383         {\r
384                 pageFound = 1;\r
385                 if ((scsiDev.compatMode >= COMPAT_SCSI2))\r
386                 {\r
387                         pageIn(pc, idx, DisconnectReconnectPage, sizeof(DisconnectReconnectPage));\r
388                         idx += sizeof(DisconnectReconnectPage);\r
389                 }\r
390                 else\r
391                 {\r
392                         pageIn(pc, idx, DisconnectReconnectPage_SCSI1, sizeof(DisconnectReconnectPage_SCSI1));\r
393                         idx += sizeof(DisconnectReconnectPage_SCSI1);\r
394                 }\r
395         }\r
396 \r
397         if (pageCode == 0x03 || pageCode == 0x3F)\r
398         {\r
399                 pageFound = 1;\r
400                 pageIn(pc, idx, FormatDevicePage, sizeof(FormatDevicePage));\r
401                 if (pc != 0x01)\r
402                 {\r
403                         uint16_t sectorsPerTrack = scsiDev.target->cfg->sectorsPerTrack;\r
404                         scsiDev.data[idx+10] = sectorsPerTrack >> 8;\r
405                         scsiDev.data[idx+11] = sectorsPerTrack & 0xFF;\r
406 \r
407                         // Fill out the configured bytes-per-sector\r
408                         uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;\r
409                         scsiDev.data[idx+12] = bytesPerSector >> 8;\r
410                         scsiDev.data[idx+13] = bytesPerSector & 0xFF;\r
411                 }\r
412                 else\r
413                 {\r
414                         // Set a mask for the changeable values.\r
415                         scsiDev.data[idx+12] = 0xFF;\r
416                         scsiDev.data[idx+13] = 0xFF;\r
417                 }\r
418 \r
419                 idx += sizeof(FormatDevicePage);\r
420         }\r
421 \r
422         if (pageCode == 0x04 || pageCode == 0x3F)\r
423         {\r
424                 pageFound = 1;\r
425                 if ((scsiDev.compatMode >= COMPAT_SCSI2))\r
426                 {\r
427                         pageIn(pc, idx, RigidDiskDriveGeometry, sizeof(RigidDiskDriveGeometry));\r
428                 }\r
429                 else\r
430                 {\r
431                         pageIn(pc, idx, RigidDiskDriveGeometry_SCSI1, sizeof(RigidDiskDriveGeometry_SCSI1));\r
432                 }\r
433 \r
434                 if (pc != 0x01)\r
435                 {\r
436                         // Need to fill out the number of cylinders.\r
437                         uint32 cyl;\r
438                         uint8 head;\r
439                         uint32 sector;\r
440                         LBA2CHS(\r
441                                 getScsiCapacity(\r
442                                         scsiDev.target->cfg->sdSectorStart,\r
443                                         scsiDev.target->liveCfg.bytesPerSector,\r
444                                         scsiDev.target->cfg->scsiSectors),\r
445                                 &cyl,\r
446                                 &head,\r
447                                 &sector,\r
448                                 scsiDev.target->cfg->headsPerCylinder,\r
449                                 scsiDev.target->cfg->sectorsPerTrack);\r
450 \r
451                         scsiDev.data[idx+2] = cyl >> 16;\r
452                         scsiDev.data[idx+3] = cyl >> 8;\r
453                         scsiDev.data[idx+4] = cyl;\r
454 \r
455                         memcpy(&scsiDev.data[idx+6], &scsiDev.data[idx+2], 3);\r
456                         memcpy(&scsiDev.data[idx+9], &scsiDev.data[idx+2], 3);\r
457 \r
458                         scsiDev.data[idx+5] = scsiDev.target->cfg->headsPerCylinder;\r
459                 }\r
460 \r
461                 if ((scsiDev.compatMode >= COMPAT_SCSI2))\r
462                 {\r
463                         idx += sizeof(RigidDiskDriveGeometry);\r
464                 }\r
465                 else\r
466                 {\r
467                         idx += sizeof(RigidDiskDriveGeometry_SCSI1);\r
468                 }\r
469         }\r
470 \r
471         if (pageCode == 0x05 || pageCode == 0x3F)\r
472         {\r
473                 pageFound = 1;\r
474                 pageIn(pc, idx, FlexibleDiskDriveGeometry, sizeof(FlexibleDiskDriveGeometry));\r
475                 idx += sizeof(FlexibleDiskDriveGeometry);\r
476         }\r
477 \r
478         // DON'T output the following pages for SCSI1 hosts. They get upset when\r
479         // we have more data to send than the allocation length provided.\r
480         // (ie. Try not to output any more pages below this comment)\r
481 \r
482 \r
483         if ((scsiDev.compatMode >= COMPAT_SCSI2) &&\r
484                 (pageCode == 0x08 || pageCode == 0x3F))\r
485         {\r
486                 pageFound = 1;\r
487                 pageIn(pc, idx, CachingPage, sizeof(CachingPage));\r
488                 idx += sizeof(CachingPage);\r
489         }\r
490 \r
491         if ((scsiDev.compatMode >= COMPAT_SCSI2)\r
492                 && (pageCode == 0x0A || pageCode == 0x3F))\r
493         {\r
494                 pageFound = 1;\r
495                 pageIn(pc, idx, ControlModePage, sizeof(ControlModePage));\r
496                 idx += sizeof(ControlModePage);\r
497         }\r
498 \r
499         if ((scsiDev.target->cfg->deviceType == CONFIG_SEQUENTIAL) &&\r
500                 (pageCode == 0x10 || pageCode == 0x3F))\r
501         {\r
502                 pageFound = 1;\r
503                 pageIn(\r
504                         pc,\r
505                         idx,\r
506                         SequentialDeviceConfigPage,\r
507                         sizeof(SequentialDeviceConfigPage));\r
508                 idx += sizeof(SequentialDeviceConfigPage);\r
509         }\r
510 \r
511         if ((\r
512                         (scsiDev.target->cfg->quirks == CONFIG_QUIRKS_APPLE)\r
513                 ) &&\r
514                 (pageCode == 0x30 || pageCode == 0x3F))\r
515         {\r
516                 pageFound = 1;\r
517                 pageIn(pc, idx, AppleVendorPage, sizeof(AppleVendorPage));\r
518                 idx += sizeof(AppleVendorPage);\r
519         }\r
520 \r
521         if (!pageFound)\r
522         {\r
523                 // Unknown Page Code\r
524                 pageFound = 0;\r
525                 scsiDev.status = CHECK_CONDITION;\r
526                 scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
527                 scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
528                 scsiDev.phase = STATUS;\r
529         }\r
530         else\r
531         {\r
532                 // Go back and fill out the mode data length\r
533                 if (sixByteCmd)\r
534                 {\r
535                         // Cannot currently exceed limits. yay\r
536                         scsiDev.data[0] = idx - 1;\r
537                 }\r
538                 else\r
539                 {\r
540                         scsiDev.data[0] = ((idx - 2) >> 8);\r
541                         scsiDev.data[1] = (idx - 2);\r
542                 }\r
543 \r
544                 scsiDev.dataLen = idx > allocLength ? allocLength : idx;\r
545                 scsiDev.phase = DATA_IN;\r
546         }\r
547 }\r
548 \r
549 \r
550 // Callback after the DATA OUT phase is complete.\r
551 static void doModeSelect(void)\r
552 {\r
553         if (scsiDev.status == GOOD) // skip if we've already encountered an error\r
554         {\r
555                 // scsiDev.dataLen bytes are in scsiDev.data\r
556 \r
557                 int idx;\r
558                 int blockDescLen;\r
559                 if (scsiDev.cdb[0] == 0x55)\r
560                 {\r
561                         blockDescLen =\r
562                                 (((uint16_t)scsiDev.data[6]) << 8) |scsiDev.data[7];\r
563                         idx = 8;\r
564                 }\r
565                 else\r
566                 {\r
567                         blockDescLen = scsiDev.data[3];\r
568                         idx = 4;\r
569                 }\r
570 \r
571                 // The unwritten rule.  Blocksizes are normally set using the\r
572                 // block descriptor value, not by changing page 0x03.\r
573                 if (blockDescLen >= 8)\r
574                 {\r
575                         uint32_t bytesPerSector =\r
576                                 (((uint32_t)scsiDev.data[idx+5]) << 16) |\r
577                                 (((uint32_t)scsiDev.data[idx+6]) << 8) |\r
578                                 scsiDev.data[idx+7];\r
579                         if ((bytesPerSector < MIN_SECTOR_SIZE) ||\r
580                                 (bytesPerSector > MAX_SECTOR_SIZE))\r
581                         {\r
582                                 goto bad;\r
583                         }\r
584                         else\r
585                         {\r
586                                 scsiDev.target->liveCfg.bytesPerSector = bytesPerSector;\r
587                                 if (bytesPerSector != scsiDev.target->cfg->bytesPerSector)\r
588                                 {\r
589                                         configSave(scsiDev.target->targetId, bytesPerSector);\r
590                                 }\r
591                         }\r
592                 }\r
593                 idx += blockDescLen;\r
594 \r
595                 while (idx < scsiDev.dataLen)\r
596                 {\r
597                         int pageLen = scsiDev.data[idx + 1];\r
598                         if (idx + 2 + pageLen > scsiDev.dataLen) goto bad;\r
599 \r
600                         int pageCode = scsiDev.data[idx] & 0x3F;\r
601                         switch (pageCode)\r
602                         {\r
603                         case 0x03: // Format Device Page\r
604                         {\r
605                                 if (pageLen != 0x16) goto bad;\r
606 \r
607                                 // Fill out the configured bytes-per-sector\r
608                                 uint16_t bytesPerSector =\r
609                                         (((uint16_t)scsiDev.data[idx+12]) << 8) |\r
610                                         scsiDev.data[idx+13];\r
611 \r
612                                 // Sane values only, ok ?\r
613                                 if ((bytesPerSector < MIN_SECTOR_SIZE) ||\r
614                                         (bytesPerSector > MAX_SECTOR_SIZE))\r
615                                 {\r
616                                         goto bad;\r
617                                 }\r
618 \r
619                                 scsiDev.target->liveCfg.bytesPerSector = bytesPerSector;\r
620                                 if (scsiDev.cdb[1] & 1) // SP Save Pages flag\r
621                                 {\r
622                                         configSave(scsiDev.target->targetId, bytesPerSector);\r
623                                 }\r
624                         }\r
625                         break;\r
626                         //default:\r
627 \r
628                                 // Easiest to just ignore for now. We'll get here when changing\r
629                                 // the SCSI block size via the descriptor header.\r
630                         }\r
631                         idx += 2 + pageLen;\r
632                 }\r
633         }\r
634 \r
635         goto out;\r
636 bad:\r
637         scsiDev.status = CHECK_CONDITION;\r
638         scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
639         scsiDev.target->sense.asc = INVALID_FIELD_IN_PARAMETER_LIST;\r
640 \r
641 out:\r
642         scsiDev.phase = STATUS;\r
643 }\r
644 \r
645 int scsiModeCommand()\r
646 {\r
647         int commandHandled = 1;\r
648 \r
649         uint8 command = scsiDev.cdb[0];\r
650 \r
651         // We don't currently support the setting of any parameters.\r
652         // (ie. no MODE SELECT(6) or MODE SELECT(10) commands)\r
653 \r
654         if (command == 0x1A)\r
655         {\r
656                 // MODE SENSE(6)\r
657                 int dbd = scsiDev.cdb[1] & 0x08; // Disable block descriptors\r
658                 int pc = scsiDev.cdb[2] >> 6; // Page Control\r
659                 int pageCode = scsiDev.cdb[2] & 0x3F;\r
660                 int allocLength = scsiDev.cdb[4];\r
661 \r
662                 // SCSI1 standard: (CCS X3T9.2/86-52)\r
663                 // "An Allocation Length of zero indicates that no MODE SENSE data shall\r
664                 // be transferred. This condition shall not be considered as an error."\r
665                 doModeSense(1, dbd, pc, pageCode, allocLength);\r
666         }\r
667         else if (command == 0x5A)\r
668         {\r
669                 // MODE SENSE(10)\r
670                 int dbd = scsiDev.cdb[1] & 0x08; // Disable block descriptors\r
671                 int pc = scsiDev.cdb[2] >> 6; // Page Control\r
672                 int pageCode = scsiDev.cdb[2] & 0x3F;\r
673                 int allocLength =\r
674                         (((uint16) scsiDev.cdb[7]) << 8) +\r
675                         scsiDev.cdb[8];\r
676                 doModeSense(0, dbd, pc, pageCode, allocLength);\r
677         }\r
678         else if (command == 0x15)\r
679         {\r
680                 // MODE SELECT(6)\r
681                 int len = scsiDev.cdb[4];\r
682                 if (len == 0)\r
683                 {\r
684                         // If len == 0, then transfer no data. From the SCSI 2 standard:\r
685                         //      A parameter list length of zero indicates that no data shall\r
686                         //      be transferred. This condition shall not be considered as an\r
687                         //              error.\r
688                         scsiDev.phase = STATUS;\r
689                 }\r
690                 else\r
691                 {\r
692                         scsiDev.dataLen = len;\r
693                         scsiDev.phase = DATA_OUT;\r
694                         scsiDev.postDataOutHook = doModeSelect;\r
695                 }\r
696         }\r
697         else if (command == 0x55)\r
698         {\r
699                 // MODE SELECT(10)\r
700                 int allocLength = (((uint16) scsiDev.cdb[7]) << 8) + scsiDev.cdb[8];\r
701                 if (allocLength == 0)\r
702                 {\r
703                         // If len == 0, then transfer no data. From the SCSI 2 standard:\r
704                         //      A parameter list length of zero indicates that no data shall\r
705                         //      be transferred. This condition shall not be considered as an\r
706                         //              error.\r
707                         scsiDev.phase = STATUS;\r
708                 }\r
709                 else\r
710                 {\r
711                         scsiDev.dataLen = allocLength;\r
712                         scsiDev.phase = DATA_OUT;\r
713                         scsiDev.postDataOutHook = doModeSelect;\r
714                 }\r
715         }\r
716         else\r
717         {\r
718                 commandHandled = 0;\r
719         }\r
720 \r
721         return commandHandled;\r
722 }\r