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