mirror of
https://github.com/fhgwright/SCSI2SD.git
synced 2024-06-12 12:29:28 +00:00
794 lines
21 KiB
C++
Executable File
794 lines
21 KiB
C++
Executable File
// Copyright (C) 2014 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/>.
|
|
|
|
#include "ConfigUtil.hh"
|
|
|
|
#include <limits>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
|
|
#include <string.h>
|
|
|
|
#include <wx/wxprec.h>
|
|
#ifndef WX_PRECOMP
|
|
#include <wx/wx.h>
|
|
#endif
|
|
#include <wx/base64.h>
|
|
#include <wx/buffer.h>
|
|
#include <wx/xml/xml.h>
|
|
|
|
|
|
using namespace SCSI2SD;
|
|
|
|
namespace
|
|
{
|
|
// Endian conversion routines.
|
|
// The Cortex-M3 inside the Cypress PSoC 5LP is a
|
|
// little-endian device.
|
|
|
|
bool isHostLE()
|
|
{
|
|
union
|
|
{
|
|
int i;
|
|
char c[sizeof(int)];
|
|
} x;
|
|
x.i = 1;
|
|
return (x.c[0] == 1);
|
|
}
|
|
|
|
uint16_t toLE16(uint16_t in)
|
|
{
|
|
if (isHostLE())
|
|
{
|
|
return in;
|
|
}
|
|
else
|
|
{
|
|
return (in >> 8) | (in << 8);
|
|
}
|
|
}
|
|
uint16_t fromLE16(uint16_t in)
|
|
{
|
|
return toLE16(in);
|
|
}
|
|
|
|
uint32_t toLE32(uint32_t in)
|
|
{
|
|
if (isHostLE())
|
|
{
|
|
return in;
|
|
}
|
|
else
|
|
{
|
|
return (in >> 24) |
|
|
((in >> 8) & 0xff00) |
|
|
((in << 8) & 0xff0000) |
|
|
(in << 24);
|
|
}
|
|
}
|
|
uint32_t fromLE32(uint32_t in)
|
|
{
|
|
return toLE32(in);
|
|
}
|
|
|
|
std::vector<uint8_t> getModePages(const TargetConfig& cfg)
|
|
{
|
|
std::vector<uint8_t> result;
|
|
unsigned i = 0;
|
|
while (i < sizeof(cfg.modePages) - 2)
|
|
{
|
|
int pageLen = cfg.modePages[i+1];
|
|
if (pageLen == 0) break;
|
|
std::copy(
|
|
&cfg.modePages[i],
|
|
&cfg.modePages[i+pageLen+2],
|
|
std::back_inserter(result));
|
|
i += pageLen + 2;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<uint8_t> getVPDPages(const TargetConfig& cfg)
|
|
{
|
|
std::vector<uint8_t> result;
|
|
unsigned i = 0;
|
|
while (i < sizeof(cfg.vpd) - 4)
|
|
{
|
|
int pageLen = cfg.vpd[i+3];
|
|
if (pageLen == 0) break;
|
|
std::copy(
|
|
&cfg.vpd[i],
|
|
&cfg.vpd[i+pageLen+4],
|
|
std::back_inserter(result));
|
|
i += pageLen + 4;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
BoardConfig
|
|
ConfigUtil::DefaultBoardConfig()
|
|
{
|
|
BoardConfig config;
|
|
memset(&config, 0, sizeof(config));
|
|
|
|
memcpy(config.magic, "BCFG", 4);
|
|
|
|
|
|
// Default to maximum fail-safe options.
|
|
config.flags = 0;
|
|
config.flags6 = S2S_CFG_ENABLE_TERMINATOR;
|
|
config.selectionDelay = 255; // auto
|
|
|
|
return config;
|
|
}
|
|
|
|
TargetConfig
|
|
ConfigUtil::Default(size_t targetIdx)
|
|
{
|
|
TargetConfig config;
|
|
memset(&config, 0, sizeof(config));
|
|
|
|
config.scsiId = targetIdx;
|
|
if (targetIdx == 0)
|
|
{
|
|
config.scsiId = config.scsiId | CONFIG_TARGET_ENABLED;
|
|
}
|
|
config.deviceType = CONFIG_FIXED;
|
|
|
|
// Default to maximum fail-safe options.
|
|
config.flagsDEPRECATED = 0;
|
|
config.deviceTypeModifier = 0;
|
|
config.sdSectorStart = 0;
|
|
|
|
// Default to 2GB. Many systems have trouble with > 2GB disks, and
|
|
// a few start to complain at 1GB.
|
|
config.scsiSectors = 4194303; // 2GB - 1 sector
|
|
config.bytesPerSector = 512;
|
|
config.sectorsPerTrack = 63;
|
|
config.headsPerCylinder = 255;
|
|
memcpy(config.vendor, " codesrc", 8);
|
|
memcpy(config.prodId, " SCSI2SD", 16);
|
|
memcpy(config.revision, " 4.2", 4);
|
|
memcpy(config.serial, "1234567812345678", 16);
|
|
|
|
// Reserved fields, already set to 0
|
|
// config.reserved
|
|
|
|
// not supported yet.
|
|
// config.vpd
|
|
|
|
return config;
|
|
}
|
|
|
|
|
|
TargetConfig
|
|
ConfigUtil::fromBytes(const uint8_t* data)
|
|
{
|
|
TargetConfig result;
|
|
memcpy(&result, data, sizeof(TargetConfig));
|
|
result.sdSectorStart = toLE32(result.sdSectorStart);
|
|
result.scsiSectors = toLE32(result.scsiSectors);
|
|
result.bytesPerSector = toLE16(result.bytesPerSector);
|
|
result.sectorsPerTrack = toLE16(result.sectorsPerTrack);
|
|
result.headsPerCylinder = toLE16(result.headsPerCylinder);
|
|
return result;
|
|
}
|
|
|
|
|
|
std::vector<uint8_t>
|
|
ConfigUtil::toBytes(const TargetConfig& _config)
|
|
{
|
|
TargetConfig config(_config);
|
|
config.sdSectorStart = fromLE32(config.sdSectorStart);
|
|
config.scsiSectors = fromLE32(config.scsiSectors);
|
|
config.bytesPerSector = fromLE16(config.bytesPerSector);
|
|
config.sectorsPerTrack = fromLE16(config.sectorsPerTrack);
|
|
config.headsPerCylinder = fromLE16(config.headsPerCylinder);
|
|
|
|
const uint8_t* begin = reinterpret_cast<const uint8_t*>(&config);
|
|
return std::vector<uint8_t>(begin, begin + sizeof(config));
|
|
}
|
|
|
|
BoardConfig
|
|
ConfigUtil::boardConfigFromBytes(const uint8_t* data)
|
|
{
|
|
BoardConfig result;
|
|
memcpy(&result, data, sizeof(BoardConfig));
|
|
|
|
if (memcmp("BCFG", result.magic, 4))
|
|
{
|
|
return DefaultBoardConfig();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
std::vector<uint8_t>
|
|
ConfigUtil::boardConfigToBytes(const BoardConfig& _config)
|
|
{
|
|
BoardConfig config(_config);
|
|
|
|
memcpy(config.magic, "BCFG", 4);
|
|
const uint8_t* begin = reinterpret_cast<const uint8_t*>(&config);
|
|
return std::vector<uint8_t>(begin, begin + sizeof(config));
|
|
}
|
|
|
|
std::string
|
|
ConfigUtil::toXML(const TargetConfig& config)
|
|
{
|
|
std::stringstream s;
|
|
std::vector<uint8_t> modePages(getModePages(config));
|
|
std::vector<uint8_t> vpd(getVPDPages(config));
|
|
|
|
s <<
|
|
"<SCSITarget id=\"" <<
|
|
static_cast<int>(config.scsiId & CONFIG_TARGET_ID_BITS) << "\">\n" <<
|
|
|
|
" <enabled>" <<
|
|
(config.scsiId & CONFIG_TARGET_ENABLED ? "true" : "false") <<
|
|
"</enabled>\n" <<
|
|
|
|
"\n" <<
|
|
" <!-- ********************************************************\n" <<
|
|
" Space separated list. Available options:\n" <<
|
|
" apple\t\tReturns Apple-specific mode pages\n" <<
|
|
" omti\t\tOMTI host non-standard link control\n" <<
|
|
" xebec\t\tXEBEC ignore step options in control byte\n" <<
|
|
" vms\t\tVMS output max 254 bytes inquiry data\n" <<
|
|
" ********************************************************* -->\n" <<
|
|
" <quirks>";
|
|
if (config.quirks == CONFIG_QUIRKS_APPLE)
|
|
{
|
|
s << "apple";
|
|
}
|
|
else if (config.quirks == CONFIG_QUIRKS_OMTI)
|
|
{
|
|
s << "omti";
|
|
}
|
|
else if (config.quirks == CONFIG_QUIRKS_XEBEC)
|
|
{
|
|
s << "xebec";
|
|
}
|
|
else if (config.quirks == CONFIG_QUIRKS_VMS)
|
|
{
|
|
s << "vms";
|
|
}
|
|
|
|
s <<
|
|
"</quirks>\n" <<
|
|
|
|
"\n\n" <<
|
|
" <!-- ********************************************************\n" <<
|
|
" 0x0 Fixed hard drive.\n" <<
|
|
" 0x1 Removable drive.\n" <<
|
|
" 0x2 Optical drive (ie. CD drive).\n" <<
|
|
" 0x3 1.44MB Floppy Drive.\n" <<
|
|
" ********************************************************* -->\n" <<
|
|
" <deviceType>0x" <<
|
|
std::hex << static_cast<int>(config.deviceType) <<
|
|
"</deviceType>\n" <<
|
|
|
|
"\n\n" <<
|
|
" <!-- ********************************************************\n" <<
|
|
" Device type modifier is usually 0x00. Only change this if your\n" <<
|
|
" OS requires some special value.\n" <<
|
|
"\n" <<
|
|
" 0x4C Data General Micropolis disk\n" <<
|
|
" ********************************************************* -->\n" <<
|
|
" <deviceTypeModifier>0x" <<
|
|
std::hex << static_cast<int>(config.deviceTypeModifier) <<
|
|
"</deviceTypeModifier>\n" <<
|
|
|
|
"\n\n" <<
|
|
" <!-- ********************************************************\n" <<
|
|
" SD card offset, as a sector number (always 512 bytes).\n" <<
|
|
" ********************************************************* -->\n" <<
|
|
" <sdSectorStart>" << std::dec << config.sdSectorStart << "</sdSectorStart>\n" <<
|
|
"\n\n" <<
|
|
" <!-- ********************************************************\n" <<
|
|
" Drive geometry settings.\n" <<
|
|
" ********************************************************* -->\n" <<
|
|
"\n"
|
|
" <scsiSectors>" << std::dec << config.scsiSectors << "</scsiSectors>\n" <<
|
|
" <bytesPerSector>" << std::dec << config.bytesPerSector << "</bytesPerSector>\n" <<
|
|
" <sectorsPerTrack>" << std::dec << config.sectorsPerTrack<< "</sectorsPerTrack>\n" <<
|
|
" <headsPerCylinder>" << std::dec << config.headsPerCylinder << "</headsPerCylinder>\n" <<
|
|
"\n\n" <<
|
|
" <!-- ********************************************************\n" <<
|
|
" Drive identification information. The SCSI2SD doesn't\n" <<
|
|
" care what these are set to. Use these strings to trick a OS\n" <<
|
|
" thinking a specific hard drive model is attached.\n" <<
|
|
" ********************************************************* -->\n" <<
|
|
"\n"
|
|
" <!-- 8 character vendor string -->\n" <<
|
|
" <!-- For Apple HD SC Setup/Drive Setup, use ' SEAGATE' -->\n" <<
|
|
" <vendor>" << std::string(config.vendor, 8) << "</vendor>\n" <<
|
|
"\n" <<
|
|
" <!-- 16 character produce identifier -->\n" <<
|
|
" <!-- For Apple HD SC Setup/Drive Setup, use ' ST225N' -->\n" <<
|
|
" <prodId>" << std::string(config.prodId, 16) << "</prodId>\n" <<
|
|
"\n" <<
|
|
" <!-- 4 character product revision number -->\n" <<
|
|
" <!-- For Apple HD SC Setup/Drive Setup, use '1.0 ' -->\n" <<
|
|
" <revision>" << std::string(config.revision, 4) << "</revision>\n" <<
|
|
"\n" <<
|
|
" <!-- 16 character serial number -->\n" <<
|
|
" <serial>" << std::string(config.serial, 16) << "</serial>\n" <<
|
|
"\n" <<
|
|
" <!-- Custom mode pages, base64 encoded, up to 1024 bytes.-->\n" <<
|
|
" <modePages>\n" <<
|
|
(modePages.size() == 0 ? "" :
|
|
wxBase64Encode(&modePages[0], modePages.size())) <<
|
|
"\n" <<
|
|
" </modePages>\n" <<
|
|
"\n" <<
|
|
" <!-- Custom inquiry VPD pages, base64 encoded, up to 1024 bytes.-->\n" <<
|
|
" <vpd>\n" <<
|
|
(vpd.size() == 0 ? "" :
|
|
wxBase64Encode(&vpd[0], vpd.size())) <<
|
|
"\n" <<
|
|
" </vpd>\n" <<
|
|
"</SCSITarget>\n";
|
|
|
|
return s.str();
|
|
}
|
|
|
|
std::string
|
|
ConfigUtil::toXML(const BoardConfig& config)
|
|
{
|
|
std::stringstream s;
|
|
|
|
s << "<BoardConfig>\n" <<
|
|
|
|
" <!-- ********************************************************\n" <<
|
|
" Enable the onboard active terminator (v5.1 or greater).\n"
|
|
" Both ends of the SCSI chain should be terminated. Disable\n" <<
|
|
" only if the SCSI2SD is in the middle of a chain with other\n" <<
|
|
" devices.\n" <<
|
|
" ********************************************************* -->\n" <<
|
|
" <enableTerminator>" <<
|
|
(config.flags6 & S2S_CFG_ENABLE_TERMINATOR ? "true" : "false") <<
|
|
"</enableTerminator>\n" <<
|
|
|
|
" <unitAttention>" <<
|
|
(config.flags & CONFIG_ENABLE_UNIT_ATTENTION ? "true" : "false") <<
|
|
"</unitAttention>\n" <<
|
|
|
|
" <parity>" <<
|
|
(config.flags & CONFIG_ENABLE_PARITY ? "true" : "false") <<
|
|
"</parity>\n" <<
|
|
|
|
" <!-- ********************************************************\n" <<
|
|
" Only set to true when using with a fast SCSI2 host\n " <<
|
|
" controller. This can cause problems with older/slower\n" <<
|
|
" hardware.\n" <<
|
|
" ********************************************************* -->\n" <<
|
|
" <enableScsi2>" <<
|
|
(config.flags & CONFIG_ENABLE_SCSI2 ? "true" : "false") <<
|
|
"</enableScsi2>\n" <<
|
|
|
|
" <!-- ********************************************************\n" <<
|
|
" Setting to 'true' will result in increased performance at the\n" <<
|
|
" cost of lower noise immunity.\n" <<
|
|
" Only set to true when using short cables with only 1 or two\n" <<
|
|
" devices. This should remain off when using external SCSI1 DB25\n" <<
|
|
" cables.\n" <<
|
|
" ********************************************************* -->\n" <<
|
|
" <disableGlitchFilter>" <<
|
|
(config.flags & CONFIG_DISABLE_GLITCH ? "true" : "false") <<
|
|
"</disableGlitchFilter>\n" <<
|
|
|
|
" <enableCache>" <<
|
|
(config.flags & CONFIG_ENABLE_CACHE ? "true" : "false") <<
|
|
"</enableCache>\n" <<
|
|
|
|
" <enableDisconnect>" <<
|
|
(config.flags & CONFIG_ENABLE_DISCONNECT ? "true" : "false") <<
|
|
"</enableDisconnect>\n" <<
|
|
|
|
" <!-- ********************************************************\n" <<
|
|
" Respond to very short duration selection attempts. This supports\n" <<
|
|
" non-standard hardware, but is generally safe to enable.\n" <<
|
|
" Required for Philips P2000C.\n" <<
|
|
" ********************************************************* -->\n" <<
|
|
" <selLatch>" <<
|
|
(config.flags & CONFIG_ENABLE_SEL_LATCH? "true" : "false") <<
|
|
"</selLatch>\n" <<
|
|
|
|
|
|
" <!-- ********************************************************\n" <<
|
|
" Convert luns to IDs. The unit must already be configured to respond\n" <<
|
|
" on the ID. Allows dual drives to be accessed from a \n" <<
|
|
" XEBEC S1410 SASI bridge.\n" <<
|
|
" eg. Configured for dual drives as IDs 0 and 1, but the XEBEC will\n" <<
|
|
" access the second disk as ID0, lun 1.\n" <<
|
|
" See ttp://bitsavers.trailing-edge.com/pdf/xebec/104524C_S1410Man_Aug83.pdf\n" <<
|
|
" ********************************************************* -->\n" <<
|
|
" <mapLunsToIds>" <<
|
|
(config.flags & CONFIG_MAP_LUNS_TO_IDS ? "true" : "false") <<
|
|
"</mapLunsToIds>\n" <<
|
|
|
|
" <!-- ********************************************************\n" <<
|
|
" Delay (in milliseconds) before responding to a SCSI selection.\n" <<
|
|
" 255 (auto) sets it to 0 for SCSI2 hosts and 1ms otherwise.\n" <<
|
|
" Some samplers need this set to 1 manually.\n" <<
|
|
" ********************************************************* -->\n" <<
|
|
" <selectionDelay>" << static_cast<int>(config.selectionDelay) << "</selectionDelay>\n" <<
|
|
|
|
" <!-- ********************************************************\n" <<
|
|
" Startup delay (in seconds) before responding to the SCSI bus \n" <<
|
|
" after power on. Default = 0.\n" <<
|
|
" ********************************************************* -->\n" <<
|
|
" <startupDelay>" << static_cast<int>(config.startupDelay) << "</startupDelay>\n" <<
|
|
|
|
" <!-- ********************************************************\n" <<
|
|
" Speed limit the SCSI interface. This is the -max- speed the \n" <<
|
|
" device will run at. The actual spee depends on the capability\n" <<
|
|
" of the host controller.\n" <<
|
|
" 0 No limit\n" <<
|
|
" 1 Async 1.5MB/s\n" <<
|
|
" ********************************************************* -->\n" <<
|
|
" <scsiSpeed>" << static_cast<int>(config.scsiSpeed) << "</scsiSpeed>\n" <<
|
|
"</BoardConfig>\n";
|
|
|
|
return s.str();
|
|
}
|
|
|
|
|
|
static uint64_t parseInt(wxXmlNode* node, uint64_t limit)
|
|
{
|
|
std::string str(node->GetNodeContent().mb_str());
|
|
if (str.empty())
|
|
{
|
|
throw std::runtime_error("Empty " + node->GetName());
|
|
}
|
|
|
|
std::stringstream s;
|
|
if (str.find("0x") == 0)
|
|
{
|
|
s << std::hex << str.substr(2);
|
|
}
|
|
else
|
|
{
|
|
s << str;
|
|
}
|
|
|
|
uint64_t result;
|
|
s >> result;
|
|
if (!s)
|
|
{
|
|
throw std::runtime_error("Invalid value for " + node->GetName());
|
|
}
|
|
|
|
if (result > limit)
|
|
{
|
|
std::stringstream msg;
|
|
msg << "Invalid value for " << node->GetName() <<
|
|
" (max=" << limit << ")";
|
|
throw std::runtime_error(msg.str());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static TargetConfig
|
|
parseTarget(wxXmlNode* node)
|
|
{
|
|
int id;
|
|
{
|
|
std::stringstream s;
|
|
s << node->GetAttribute("id", "7");
|
|
s >> id;
|
|
if (!s) throw std::runtime_error("Could not parse SCSITarget id attr");
|
|
}
|
|
TargetConfig result = ConfigUtil::Default(id & 0x7);
|
|
|
|
wxXmlNode *child = node->GetChildren();
|
|
while (child)
|
|
{
|
|
if (child->GetName() == "enabled")
|
|
{
|
|
std::string s(child->GetNodeContent().mb_str());
|
|
if (s == "true")
|
|
{
|
|
result.scsiId |= CONFIG_TARGET_ENABLED;
|
|
}
|
|
else
|
|
{
|
|
result.scsiId = result.scsiId & ~CONFIG_TARGET_ENABLED;
|
|
}
|
|
}
|
|
else if (child->GetName() == "quirks")
|
|
{
|
|
std::stringstream s(std::string(child->GetNodeContent().mb_str()));
|
|
std::string quirk;
|
|
while (s >> quirk)
|
|
{
|
|
if (quirk == "apple")
|
|
{
|
|
result.quirks |= CONFIG_QUIRKS_APPLE;
|
|
}
|
|
else if (quirk == "omti")
|
|
{
|
|
result.quirks |= CONFIG_QUIRKS_OMTI;
|
|
}
|
|
else if (quirk == "xebec")
|
|
{
|
|
result.quirks |= CONFIG_QUIRKS_XEBEC;
|
|
}
|
|
else if (quirk == "vms")
|
|
{
|
|
result.quirks |= CONFIG_QUIRKS_VMS;
|
|
}
|
|
}
|
|
}
|
|
else if (child->GetName() == "deviceType")
|
|
{
|
|
result.deviceType = parseInt(child, 0xFF);
|
|
}
|
|
else if (child->GetName() == "deviceTypeModifier")
|
|
{
|
|
result.deviceTypeModifier = parseInt(child, 0xFF);
|
|
}
|
|
else if (child->GetName() == "sdSectorStart")
|
|
{
|
|
result.sdSectorStart = parseInt(child, 0xFFFFFFFF);
|
|
}
|
|
else if (child->GetName() == "scsiSectors")
|
|
{
|
|
result.scsiSectors = parseInt(child, 0xFFFFFFFF);
|
|
}
|
|
else if (child->GetName() == "bytesPerSector")
|
|
{
|
|
result.bytesPerSector = parseInt(child, 8192);
|
|
}
|
|
else if (child->GetName() == "sectorsPerTrack")
|
|
{
|
|
result.sectorsPerTrack = parseInt(child, 255);
|
|
}
|
|
else if (child->GetName() == "headsPerCylinder")
|
|
{
|
|
result.headsPerCylinder = parseInt(child, 255);
|
|
}
|
|
else if (child->GetName() == "vendor")
|
|
{
|
|
std::string s(child->GetNodeContent().mb_str());
|
|
s = s.substr(0, sizeof(result.vendor));
|
|
memset(result.vendor, ' ', sizeof(result.vendor));
|
|
memcpy(result.vendor, s.c_str(), s.size());
|
|
}
|
|
else if (child->GetName() == "prodId")
|
|
{
|
|
std::string s(child->GetNodeContent().mb_str());
|
|
s = s.substr(0, sizeof(result.prodId));
|
|
memset(result.prodId, ' ', sizeof(result.prodId));
|
|
memcpy(result.prodId, s.c_str(), s.size());
|
|
}
|
|
else if (child->GetName() == "revision")
|
|
{
|
|
std::string s(child->GetNodeContent().mb_str());
|
|
s = s.substr(0, sizeof(result.revision));
|
|
memset(result.revision, ' ', sizeof(result.revision));
|
|
memcpy(result.revision, s.c_str(), s.size());
|
|
}
|
|
else if (child->GetName() == "serial")
|
|
{
|
|
std::string s(child->GetNodeContent().mb_str());
|
|
s = s.substr(0, sizeof(result.serial));
|
|
memset(result.serial, ' ', sizeof(result.serial));
|
|
memcpy(result.serial, s.c_str(), s.size());
|
|
}
|
|
else if (child->GetName() == "modePages")
|
|
{
|
|
wxMemoryBuffer buf =
|
|
wxBase64Decode(child->GetNodeContent(), wxBase64DecodeMode_SkipWS);
|
|
size_t len = std::min(buf.GetDataLen(), sizeof(result.modePages));
|
|
memcpy(result.modePages, buf.GetData(), len);
|
|
}
|
|
else if (child->GetName() == "vpd")
|
|
{
|
|
wxMemoryBuffer buf =
|
|
wxBase64Decode(child->GetNodeContent(), wxBase64DecodeMode_SkipWS);
|
|
size_t len = std::min(buf.GetDataLen(), sizeof(result.vpd));
|
|
memcpy(result.vpd, buf.GetData(), len);
|
|
}
|
|
|
|
|
|
|
|
child = child->GetNext();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static BoardConfig
|
|
parseBoardConfig(wxXmlNode* node)
|
|
{
|
|
BoardConfig result = ConfigUtil::DefaultBoardConfig();
|
|
|
|
wxXmlNode *child = node->GetChildren();
|
|
while (child)
|
|
{
|
|
if (child->GetName() == "selectionDelay")
|
|
{
|
|
result.selectionDelay = parseInt(child, 255);
|
|
}
|
|
else if (child->GetName() == "startupDelay")
|
|
{
|
|
result.startupDelay = parseInt(child, 255);
|
|
}
|
|
else if (child->GetName() == "unitAttention")
|
|
{
|
|
std::string s(child->GetNodeContent().mb_str());
|
|
if (s == "true")
|
|
{
|
|
result.flags |= CONFIG_ENABLE_UNIT_ATTENTION;
|
|
}
|
|
else
|
|
{
|
|
result.flags = result.flags & ~CONFIG_ENABLE_UNIT_ATTENTION;
|
|
}
|
|
}
|
|
else if (child->GetName() == "parity")
|
|
{
|
|
std::string s(child->GetNodeContent().mb_str());
|
|
if (s == "true")
|
|
{
|
|
result.flags |= CONFIG_ENABLE_PARITY;
|
|
}
|
|
else
|
|
{
|
|
result.flags = result.flags & ~CONFIG_ENABLE_PARITY;
|
|
}
|
|
}
|
|
else if (child->GetName() == "enableScsi2")
|
|
{
|
|
std::string s(child->GetNodeContent().mb_str());
|
|
if (s == "true")
|
|
{
|
|
result.flags |= CONFIG_ENABLE_SCSI2;
|
|
}
|
|
else
|
|
{
|
|
result.flags = result.flags & ~CONFIG_ENABLE_SCSI2;
|
|
}
|
|
}
|
|
else if (child->GetName() == "disableGlitchFilter")
|
|
{
|
|
std::string s(child->GetNodeContent().mb_str());
|
|
if (s == "true")
|
|
{
|
|
result.flags |= CONFIG_DISABLE_GLITCH;
|
|
}
|
|
else
|
|
{
|
|
result.flags = result.flags & ~CONFIG_DISABLE_GLITCH;
|
|
}
|
|
}
|
|
else if (child->GetName() == "enableCache")
|
|
{
|
|
std::string s(child->GetNodeContent().mb_str());
|
|
if (s == "true")
|
|
{
|
|
result.flags |= CONFIG_ENABLE_CACHE;
|
|
}
|
|
else
|
|
{
|
|
result.flags = result.flags & ~CONFIG_ENABLE_CACHE;
|
|
}
|
|
}
|
|
else if (child->GetName() == "enableDisconnect")
|
|
{
|
|
std::string s(child->GetNodeContent().mb_str());
|
|
if (s == "true")
|
|
{
|
|
result.flags |= CONFIG_ENABLE_DISCONNECT;
|
|
}
|
|
else
|
|
{
|
|
result.flags = result.flags & ~CONFIG_ENABLE_DISCONNECT;
|
|
}
|
|
}
|
|
else if (child->GetName() == "enableTerminator")
|
|
{
|
|
std::string s(child->GetNodeContent().mb_str());
|
|
if (s == "true")
|
|
{
|
|
result.flags6 |= S2S_CFG_ENABLE_TERMINATOR;
|
|
}
|
|
else
|
|
{
|
|
result.flags6 = result.flags & ~S2S_CFG_ENABLE_TERMINATOR;
|
|
}
|
|
}
|
|
else if (child->GetName() == "selLatch")
|
|
{
|
|
std::string s(child->GetNodeContent().mb_str());
|
|
if (s == "true")
|
|
{
|
|
result.flags |= CONFIG_ENABLE_SEL_LATCH;
|
|
}
|
|
else
|
|
{
|
|
result.flags = result.flags & ~CONFIG_ENABLE_SEL_LATCH;
|
|
}
|
|
}
|
|
else if (child->GetName() == "mapLunsToIds")
|
|
{
|
|
std::string s(child->GetNodeContent().mb_str());
|
|
if (s == "true")
|
|
{
|
|
result.flags |= CONFIG_MAP_LUNS_TO_IDS;
|
|
}
|
|
else
|
|
{
|
|
result.flags = result.flags & ~CONFIG_MAP_LUNS_TO_IDS;
|
|
}
|
|
}
|
|
else if (child->GetName() == "scsiSpeed")
|
|
{
|
|
result.scsiSpeed = parseInt(child, CONFIG_SPEED_ASYNC_15);
|
|
}
|
|
child = child->GetNext();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
std::pair<BoardConfig, std::vector<TargetConfig>>
|
|
ConfigUtil::fromXML(const std::string& filename)
|
|
{
|
|
wxXmlDocument doc;
|
|
if (!doc.Load(filename))
|
|
{
|
|
throw std::runtime_error("Could not load XML file");
|
|
}
|
|
|
|
// start processing the XML file
|
|
if (doc.GetRoot()->GetName() != "SCSI2SD")
|
|
{
|
|
throw std::runtime_error("Invalid root node, expected <SCSI2SD>");
|
|
}
|
|
|
|
BoardConfig boardConfig = DefaultBoardConfig();
|
|
int boardConfigFound = 0;
|
|
|
|
std::vector<TargetConfig> targets;
|
|
wxXmlNode *child = doc.GetRoot()->GetChildren();
|
|
while (child)
|
|
{
|
|
if (child->GetName() == "SCSITarget")
|
|
{
|
|
targets.push_back(parseTarget(child));
|
|
}
|
|
else if (child->GetName() == "BoardConfig")
|
|
{
|
|
boardConfig = parseBoardConfig(child);
|
|
boardConfigFound = 1;
|
|
}
|
|
child = child->GetNext();
|
|
}
|
|
|
|
if (!boardConfigFound && targets.size() > 0)
|
|
{
|
|
boardConfig.flags = targets[0].flagsDEPRECATED;
|
|
}
|
|
return make_pair(boardConfig, targets);
|
|
}
|
|
|