-// Copyright (C) 2013 Michael McMaster <michael@codesrc.com>\r
-//\r
-// This file is part of SCSI2SD.\r
-//\r
-// SCSI2SD is free software: you can redistribute it and/or modify\r
-// it under the terms of the GNU General Public License as published by\r
-// the Free Software Foundation, either version 3 of the License, or\r
-// (at your option) any later version.\r
-//\r
-// SCSI2SD is distributed in the hope that it will be useful,\r
-// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-// GNU General Public License for more details.\r
-//\r
-// You should have received a copy of the GNU General Public License\r
-// along with SCSI2SD. If not, see <http://www.gnu.org/licenses/>.\r
-#pragma GCC push_options\r
-#pragma GCC optimize("-flto")\r
-\r
-#include "device.h"\r
-#include "scsi.h"\r
-#include "config.h"\r
-#include "inquiry.h"\r
-\r
-#include <string.h>\r
-\r
-static uint8 StandardResponse[] =\r
-{\r
-0x00, // "Direct-access device". AKA standard hard disk\r
-0x00, // device type modifier\r
-0x02, // Complies with ANSI SCSI-2.\r
-0x01, // Response format is compatible with the old CCS format.\r
-0x1f, // standard length.\r
-0, 0, // Reserved\r
-0x08 // Enable linked commands\r
-};\r
-// Vendor set by config 'c','o','d','e','s','r','c',' ',\r
-// prodId set by config'S','C','S','I','2','S','D',' ',' ',' ',' ',' ',' ',' ',' ',' ',\r
-// Revision set by config'2','.','0','a'\r
-\r
-/* For reference, here's a dump from an Apple branded 500Mb drive from 1994.\r
-$ sudo sg_inq -H /dev/sdd --len 255\r
-standard INQUIRY:\r
- 00 00 00 02 01 31 00 00 18 51 55 41 4e 54 55 4d 20 ....1...QUANTUM \r
- 10 4c 50 53 32 37 30 20 20 20 20 20 20 20 20 20 20 LPS270 \r
- 20 30 39 30 30 00 00 00 d9 b0 27 34 01 04 b3 01 1b 0900.....'4.....\r
- 30 07 00 a0 00 00 ff ......\r
- Vendor identification: QUANTUM \r
- Product identification: LPS270 \r
- Product revision level: 0900\r
-*/\r
-\r
-\r
-static const uint8 SupportedVitalPages[] =\r
-{\r
-0x00, // "Direct-access device". AKA standard hard disk\r
-0x00, // Page Code\r
-0x00, // Reserved\r
-0x04, // Page length\r
-0x00, // Support "Supported vital product data pages"\r
-0x80, // Support "Unit serial number page"\r
-0x81, // Support "Implemented operating definition page"\r
-0x82 // Support "ASCII Implemented operating definition page"\r
-};\r
-\r
-static const uint8 UnitSerialNumber[] =\r
-{\r
-0x00, // "Direct-access device". AKA standard hard disk\r
-0x80, // Page Code\r
-0x00, // Reserved\r
-0x10, // Page length\r
-'c','o','d','e','s','r','c','-','1','2','3','4','5','6','7','8'\r
-};\r
-\r
-static const uint8 ImpOperatingDefinition[] =\r
-{\r
-0x00, // "Direct-access device". AKA standard hard disk\r
-0x81, // Page Code\r
-0x00, // Reserved\r
-0x03, // Page length\r
-0x03, // Current: SCSI-2 operating definition\r
-0x03, // Default: SCSI-2 operating definition\r
-0x03 // Supported (list): SCSI-2 operating definition.\r
-};\r
-\r
-static const uint8 AscImpOperatingDefinition[] =\r
-{\r
-0x00, // "Direct-access device". AKA standard hard disk\r
-0x82, // Page Code\r
-0x00, // Reserved\r
-0x07, // Page length\r
-0x06, // Ascii length\r
-'S','C','S','I','-','2'\r
-};\r
-\r
-static void useCustomVPD(const TargetConfig* cfg, int pageCode)\r
-{\r
- int cfgIdx = 0;\r
- int found = 0;\r
- while ((cfgIdx < sizeof(cfg->vpd) - 4) &&\r
- (cfg->vpd[cfgIdx + 3] != 0)\r
- )\r
- {\r
- int pageSize = cfg->vpd[cfgIdx + 3] + 4;\r
- int dataPageCode = cfg->vpd[cfgIdx + 1];\r
- if (dataPageCode == pageCode)\r
- {\r
- memcpy(scsiDev.data, &(cfg->vpd[cfgIdx]), pageSize);\r
- scsiDev.dataLen = pageSize;\r
- scsiDev.phase = DATA_IN;\r
- found = 1;\r
- break;\r
- }\r
- cfgIdx += pageSize;\r
- }\r
-\r
- if (!found)\r
- {\r
- // error.\r
- scsiDev.status = CHECK_CONDITION;\r
- scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
- scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
- scsiDev.phase = STATUS;\r
- }\r
-}\r
-\r
-void scsiInquiry()\r
-{\r
- uint8 evpd = scsiDev.cdb[1] & 1; // enable vital product data.\r
- uint8 pageCode = scsiDev.cdb[2];\r
- uint32 allocationLength = scsiDev.cdb[4];\r
-\r
- // SASI standard, X3T9.3_185_RevE states that 0 == 256 bytes\r
- if (allocationLength == 0) allocationLength = 256;\r
-\r
- if (!evpd)\r
- {\r
- if (pageCode)\r
- {\r
- // error.\r
- scsiDev.status = CHECK_CONDITION;\r
- scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
- scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
- scsiDev.phase = STATUS;\r
- }\r
- else\r
- {\r
- const TargetConfig* config = scsiDev.target->cfg;\r
- memcpy(scsiDev.data, StandardResponse, sizeof(StandardResponse));\r
- scsiDev.data[1] = scsiDev.target->cfg->deviceTypeModifier;\r
- memcpy(&scsiDev.data[8], config->vendor, sizeof(config->vendor));\r
- memcpy(&scsiDev.data[16], config->prodId, sizeof(config->prodId));\r
- memcpy(&scsiDev.data[32], config->revision, sizeof(config->revision));\r
- scsiDev.dataLen = sizeof(StandardResponse) +\r
- sizeof(config->vendor) +\r
- sizeof(config->prodId) +\r
- sizeof(config->revision);\r
- scsiDev.phase = DATA_IN;\r
- }\r
- }\r
- else if (scsiDev.target->cfg->vpd[3] != 0)\r
- {\r
- useCustomVPD(scsiDev.target->cfg, pageCode);\r
- }\r
- else if (pageCode == 0x00)\r
- {\r
- memcpy(scsiDev.data, SupportedVitalPages, sizeof(SupportedVitalPages));\r
- scsiDev.dataLen = sizeof(SupportedVitalPages);\r
- scsiDev.phase = DATA_IN;\r
- }\r
- else if (pageCode == 0x80)\r
- {\r
- memcpy(scsiDev.data, UnitSerialNumber, sizeof(UnitSerialNumber));\r
- scsiDev.dataLen = sizeof(UnitSerialNumber);\r
- scsiDev.phase = DATA_IN;\r
- }\r
- else if (pageCode == 0x81)\r
- {\r
- memcpy(\r
- scsiDev.data,\r
- ImpOperatingDefinition,\r
- sizeof(ImpOperatingDefinition));\r
- scsiDev.dataLen = sizeof(ImpOperatingDefinition);\r
- scsiDev.phase = DATA_IN;\r
- }\r
- else if (pageCode == 0x82)\r
- {\r
- memcpy(\r
- scsiDev.data,\r
- AscImpOperatingDefinition,\r
- sizeof(AscImpOperatingDefinition));\r
- scsiDev.dataLen = sizeof(AscImpOperatingDefinition);\r
- scsiDev.phase = DATA_IN;\r
- }\r
- else\r
- {\r
- // error.\r
- scsiDev.status = CHECK_CONDITION;\r
- scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
- scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;\r
- scsiDev.phase = STATUS;\r
- }\r
-\r
-\r
- if (scsiDev.phase == DATA_IN)\r
- {\r
- // "real" hard drives send back exactly allocationLenth bytes, padded\r
- // with zeroes. This only seems to happen for Inquiry responses, and not\r
- // other commands that also supply an allocation length such as Mode Sense or\r
- // Request Sense.\r
- // (See below for exception to this rule when 0 allocation length)\r
- if (scsiDev.dataLen < allocationLength)\r
- {\r
- memset(\r
- &scsiDev.data[scsiDev.dataLen],\r
- 0,\r
- allocationLength - scsiDev.dataLen);\r
- }\r
- // Spec 8.2.5 requires us to simply truncate the response if it's\r
- // too big.\r
- scsiDev.dataLen = allocationLength;\r
-\r
- // Set the device type as needed.\r
- switch (scsiDev.target->cfg->deviceType)\r
- {\r
- case CONFIG_OPTICAL:\r
- scsiDev.data[0] = 0x05; // device type\r
- scsiDev.data[1] |= 0x80; // Removable bit.\r
- break;\r
-\r
- case CONFIG_SEQUENTIAL:\r
- scsiDev.data[0] = 0x01; // device type\r
- scsiDev.data[1] |= 0x80; // Removable bit.\r
- break;\r
- \r
- case CONFIG_MO:\r
- scsiDev.data[0] = 0x07; // device type\r
- scsiDev.data[1] |= 0x80; // Removable bit.\r
- break;\r
-\r
- case CONFIG_FLOPPY_14MB:\r
- case CONFIG_REMOVEABLE:\r
- scsiDev.data[1] |= 0x80; // Removable bit.\r
- break;\r
- default:\r
- // Accept defaults for a fixed disk.\r
- break;\r
- }\r
- }\r
-\r
- // Set the first byte to indicate LUN presence.\r
- if (scsiDev.lun) // We only support lun 0\r
- {\r
- scsiDev.data[0] = 0x7F;\r
- }\r
-}\r
-\r
-#pragma GCC pop_options\r
+// Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
+//
+// This file is part of SCSI2SD.
+//
+// SCSI2SD is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// SCSI2SD is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with SCSI2SD. If not, see <http://www.gnu.org/licenses/>.
+#pragma GCC push_options
+#pragma GCC optimize("-flto")
+
+#include "device.h"
+#include "scsi.h"
+#include "config.h"
+#include "inquiry.h"
+
+#include <string.h>
+
+static uint8 StandardResponse[] =
+{
+0x00, // "Direct-access device". AKA standard hard disk
+0x00, // device type modifier
+0x02, // Complies with ANSI SCSI-2.
+0x01, // Response format is compatible with the old CCS format.
+0x1f, // standard length.
+0, 0, // Reserved
+0x08 // Enable linked commands
+};
+// Vendor set by config 'c','o','d','e','s','r','c',' ',
+// prodId set by config'S','C','S','I','2','S','D',' ',' ',' ',' ',' ',' ',' ',' ',' ',
+// Revision set by config'2','.','0','a'
+
+/* For reference, here's a dump from an Apple branded 500Mb drive from 1994.
+$ sudo sg_inq -H /dev/sdd --len 255
+standard INQUIRY:
+ 00 00 00 02 01 31 00 00 18 51 55 41 4e 54 55 4d 20 ....1...QUANTUM
+ 10 4c 50 53 32 37 30 20 20 20 20 20 20 20 20 20 20 LPS270
+ 20 30 39 30 30 00 00 00 d9 b0 27 34 01 04 b3 01 1b 0900.....'4.....
+ 30 07 00 a0 00 00 ff ......
+ Vendor identification: QUANTUM
+ Product identification: LPS270
+ Product revision level: 0900
+*/
+
+
+static const uint8 SupportedVitalPages[] =
+{
+0x00, // "Direct-access device". AKA standard hard disk
+0x00, // Page Code
+0x00, // Reserved
+0x04, // Page length
+0x00, // Support "Supported vital product data pages"
+0x80, // Support "Unit serial number page"
+0x81, // Support "Implemented operating definition page"
+0x82 // Support "ASCII Implemented operating definition page"
+};
+
+static const uint8 UnitSerialNumber[] =
+{
+0x00, // "Direct-access device". AKA standard hard disk
+0x80, // Page Code
+0x00, // Reserved
+0x10, // Page length
+'c','o','d','e','s','r','c','-','1','2','3','4','5','6','7','8'
+};
+
+static const uint8 ImpOperatingDefinition[] =
+{
+0x00, // "Direct-access device". AKA standard hard disk
+0x81, // Page Code
+0x00, // Reserved
+0x03, // Page length
+0x03, // Current: SCSI-2 operating definition
+0x03, // Default: SCSI-2 operating definition
+0x03 // Supported (list): SCSI-2 operating definition.
+};
+
+static const uint8 AscImpOperatingDefinition[] =
+{
+0x00, // "Direct-access device". AKA standard hard disk
+0x82, // Page Code
+0x00, // Reserved
+0x07, // Page length
+0x06, // Ascii length
+'S','C','S','I','-','2'
+};
+
+static void useCustomVPD(const TargetConfig* cfg, int pageCode)
+{
+ int cfgIdx = 0;
+ int found = 0;
+ while ((cfgIdx < sizeof(cfg->vpd) - 4) &&
+ (cfg->vpd[cfgIdx + 3] != 0)
+ )
+ {
+ int pageSize = cfg->vpd[cfgIdx + 3] + 4;
+ int dataPageCode = cfg->vpd[cfgIdx + 1];
+ if (dataPageCode == pageCode)
+ {
+ memcpy(scsiDev.data, &(cfg->vpd[cfgIdx]), pageSize);
+ scsiDev.dataLen = pageSize;
+ scsiDev.phase = DATA_IN;
+ found = 1;
+ break;
+ }
+ cfgIdx += pageSize;
+ }
+
+ if (!found)
+ {
+ // error.
+ scsiDev.status = CHECK_CONDITION;
+ scsiDev.target->sense.code = ILLEGAL_REQUEST;
+ scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
+ scsiDev.phase = STATUS;
+ }
+}
+
+void scsiInquiry()
+{
+ uint8 evpd = scsiDev.cdb[1] & 1; // enable vital product data.
+ uint8 pageCode = scsiDev.cdb[2];
+ uint32 allocationLength = scsiDev.cdb[4];
+
+ // SASI standard, X3T9.3_185_RevE states that 0 == 256 bytes
+ // BUT SCSI 2 standard says 0 == 0.
+ if (scsiDev.compatMode <= COMPAT_SCSI1) // excludes COMPAT_SCSI2_DISABLED
+ {
+ if (allocationLength == 0) allocationLength = 256;
+ }
+
+ if (!evpd)
+ {
+ if (pageCode)
+ {
+ // error.
+ scsiDev.status = CHECK_CONDITION;
+ scsiDev.target->sense.code = ILLEGAL_REQUEST;
+ scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
+ scsiDev.phase = STATUS;
+ }
+ else
+ {
+ const TargetConfig* config = scsiDev.target->cfg;
+ memcpy(scsiDev.data, StandardResponse, sizeof(StandardResponse));
+ scsiDev.data[1] = scsiDev.target->cfg->deviceTypeModifier;
+
+ if (scsiDev.compatMode >= COMPAT_SCSI2)
+ {
+ scsiDev.data[3] = 2; // SCSI 2 response format.
+ }
+ memcpy(&scsiDev.data[8], config->vendor, sizeof(config->vendor));
+ memcpy(&scsiDev.data[16], config->prodId, sizeof(config->prodId));
+ memcpy(&scsiDev.data[32], config->revision, sizeof(config->revision));
+ scsiDev.dataLen = sizeof(StandardResponse) +
+ sizeof(config->vendor) +
+ sizeof(config->prodId) +
+ sizeof(config->revision);
+ scsiDev.phase = DATA_IN;
+ }
+ }
+ else if (scsiDev.target->cfg->vpd[3] != 0)
+ {
+ useCustomVPD(scsiDev.target->cfg, pageCode);
+ }
+ else if (pageCode == 0x00)
+ {
+ memcpy(scsiDev.data, SupportedVitalPages, sizeof(SupportedVitalPages));
+ scsiDev.dataLen = sizeof(SupportedVitalPages);
+ scsiDev.phase = DATA_IN;
+ }
+ else if (pageCode == 0x80)
+ {
+ memcpy(scsiDev.data, UnitSerialNumber, sizeof(UnitSerialNumber));
+ scsiDev.dataLen = sizeof(UnitSerialNumber);
+ scsiDev.phase = DATA_IN;
+ }
+ else if (pageCode == 0x81)
+ {
+ memcpy(
+ scsiDev.data,
+ ImpOperatingDefinition,
+ sizeof(ImpOperatingDefinition));
+ scsiDev.dataLen = sizeof(ImpOperatingDefinition);
+ scsiDev.phase = DATA_IN;
+ }
+ else if (pageCode == 0x82)
+ {
+ memcpy(
+ scsiDev.data,
+ AscImpOperatingDefinition,
+ sizeof(AscImpOperatingDefinition));
+ scsiDev.dataLen = sizeof(AscImpOperatingDefinition);
+ scsiDev.phase = DATA_IN;
+ }
+ else
+ {
+ // error.
+ scsiDev.status = CHECK_CONDITION;
+ scsiDev.target->sense.code = ILLEGAL_REQUEST;
+ scsiDev.target->sense.asc = INVALID_FIELD_IN_CDB;
+ scsiDev.phase = STATUS;
+ }
+
+
+ if (scsiDev.phase == DATA_IN)
+ {
+ // "real" hard drives send back exactly allocationLenth bytes, padded
+ // with zeroes. This only seems to happen for Inquiry responses, and not
+ // other commands that also supply an allocation length such as Mode Sense or
+ // Request Sense.
+ // (See below for exception to this rule when 0 allocation length)
+ if (scsiDev.dataLen < allocationLength)
+ {
+ memset(
+ &scsiDev.data[scsiDev.dataLen],
+ 0,
+ allocationLength - scsiDev.dataLen);
+ }
+ // Spec 8.2.5 requires us to simply truncate the response if it's
+ // too big.
+ scsiDev.dataLen = allocationLength;
+
+ // Set the device type as needed.
+ switch (scsiDev.target->cfg->deviceType)
+ {
+ case CONFIG_OPTICAL:
+ scsiDev.data[0] = 0x05; // device type
+ scsiDev.data[1] |= 0x80; // Removable bit.
+ break;
+
+ case CONFIG_SEQUENTIAL:
+ scsiDev.data[0] = 0x01; // device type
+ scsiDev.data[1] |= 0x80; // Removable bit.
+ break;
+
+ case CONFIG_MO:
+ scsiDev.data[0] = 0x07; // device type
+ scsiDev.data[1] |= 0x80; // Removable bit.
+ break;
+
+ case CONFIG_FLOPPY_14MB:
+ case CONFIG_REMOVEABLE:
+ scsiDev.data[1] |= 0x80; // Removable bit.
+ break;
+ default:
+ // Accept defaults for a fixed disk.
+ break;
+ }
+ }
+
+ // Set the first byte to indicate LUN presence.
+ if (scsiDev.lun) // We only support lun 0
+ {
+ scsiDev.data[0] = 0x7F;
+ }
+}
+
+#pragma GCC pop_options