| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 | //	Copyright (C) 2013 Michael McMaster <michael@codesrc.com>//	Copyright (C) 2019 Landon Rodgers  <g.landon.rodgers@gmail.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/>.#include "scsi.h"#include "config.h"#include "BlueSCSI_config.h"#include "inquiry.h"#include <string.h>static uint8_t StandardResponse[] ={0x00, // "Direct-access device". AKA standard hard disk0x00, // device type modifier0x02, // Complies with ANSI SCSI-2.0x01, // Response format is compatible with the old CCS format.0x1f, // standard length.0, 0, // Reserved0x18 // Enable sync and 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'static const uint8_t SupportedVitalPages[] ={0x00, // "Direct-access device". AKA standard hard disk0x00, // Page Code0x00, // Reserved0x04, // Page length0x00, // 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_t UnitSerialNumber[] ={0x00, // "Direct-access device". AKA standard hard disk0x80, // Page Code0x00, // Reserved0x10, // Page length'c','o','d','e','s','r','c','-','1','2','3','4','5','6','7','8'};static const uint8_t ImpOperatingDefinition[] ={0x00, // "Direct-access device". AKA standard hard disk0x81, // Page Code0x00, // Reserved0x03, // Page length0x03, // Current: SCSI-2 operating definition0x03, // Default: SCSI-2 operating definition0x03 // Supported (list): SCSI-2 operating definition.};static const uint8_t AscImpOperatingDefinition[] ={0x00, // "Direct-access device". AKA standard hard disk0x82, // Page Code0x00, // Reserved0x07, // Page length0x06, // Ascii length'S','C','S','I','-','2'};void s2s_scsiInquiry(){	uint8_t evpd = scsiDev.cdb[1] & 1; // enable vital product data.	uint8_t pageCode = scsiDev.cdb[2];	uint32_t 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 S2S_TargetCfg* config = scsiDev.target->cfg;			scsiDev.dataLen =				s2s_getStandardInquiry(					config,					scsiDev.data,					sizeof(scsiDev.data));			scsiDev.phase = DATA_IN;		}	}	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);        const S2S_TargetCfg* config = scsiDev.target->cfg;        memcpy(&scsiDev.data[4], config->serial, sizeof(config->serial));		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)	{		// VAX workaround		if (allocationLength == 255 &&			(scsiDev.target->cfg->quirks & S2S_CFG_QUIRKS_VMS))		{			allocationLength = 254;		}		// "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.		scsiDev.data[0] = getDeviceTypeQualifier();		switch (scsiDev.target->cfg->deviceType)		{		case S2S_CFG_OPTICAL:			scsiDev.data[1] |= 0x80; // Removable bit.			break;		case S2S_CFG_SEQUENTIAL:			scsiDev.data[1] |= 0x80; // Removable bit.			break;		case S2S_CFG_MO:			scsiDev.data[1] |= 0x80; // Removable bit.			break;		case S2S_CFG_FLOPPY_14MB:		case S2S_CFG_REMOVEABLE:			scsiDev.data[1] |= 0x80; // Removable bit.			break;		case S2S_CFG_NETWORK:			scsiDev.data[2] = 0x01;  // Page code.			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;	}}uint32_t s2s_getStandardInquiry(	const S2S_TargetCfg* cfg, uint8_t* out, uint32_t maxlen	){	uint32_t size = 0;	uint32_t buflen = sizeof(StandardResponse);	if (buflen > maxlen) buflen = maxlen;	memcpy(out, StandardResponse, buflen);	out[1] = cfg->deviceTypeModifier;	if (!(scsiDev.boardCfg.flags & S2S_CFG_ENABLE_SCSI2))	{		out[2] = 1; // Report only SCSI 1 compliance version	}	if (scsiDev.compatMode >= COMPAT_SCSI2)	{		out[3] = 2; // SCSI 2 response format.	}	memcpy(&out[8], cfg->vendor, sizeof(cfg->vendor));	memcpy(&out[16], cfg->prodId, sizeof(cfg->prodId));	memcpy(&out[32], cfg->revision, sizeof(cfg->revision));	size = sizeof(StandardResponse) +		sizeof(cfg->vendor) +		sizeof(cfg->prodId) +		sizeof(cfg->revision);	// Mac Daynaport Driver does not like this added.	if(cfg->deviceType != S2S_CFG_NETWORK) {		memcpy(&out[36], PLATFORM_INQUIRY, sizeof(PLATFORM_INQUIRY));		size += sizeof(PLATFORM_INQUIRY);	}	return size;}uint8_t getDeviceTypeQualifier(){	// Set the device type as needed.	switch (scsiDev.target->cfg->deviceType)	{	case S2S_CFG_OPTICAL:		return 0x05;		break;	case S2S_CFG_SEQUENTIAL:		return 0x01;		break;	case S2S_CFG_MO:		return 0x07;		break;	case S2S_CFG_FLOPPY_14MB:	case S2S_CFG_REMOVEABLE:		return 0;		break;	case S2S_CFG_NETWORK:		// processor device		return 0x03;		break;	default:		// Accept defaults for a fixed disk.		return 0;	}}
 |