1 // Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
3 // This file is part of SCSI2SD.
5 // SCSI2SD is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
10 // SCSI2SD is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with SCSI2SD. If not, see <http://www.gnu.org/licenses/>.
18 #include "ConfigUtil.hh"
26 #include <wx/wxprec.h>
30 #include <wx/base64.h>
31 #include <wx/buffer.h>
32 #include <wx/xml/xml.h>
35 using namespace SCSI2SD;
39 // Endian conversion routines.
40 // The Cortex-M3 inside the Cypress PSoC 5LP is a
41 // little-endian device.
54 uint16_t toLE16(uint16_t in)
62 return (in >> 8) | (in << 8);
65 uint16_t fromLE16(uint16_t in)
70 uint32_t toLE32(uint32_t in)
79 ((in >> 8) & 0xff00) |
80 ((in << 8) & 0xff0000) |
84 uint32_t fromLE32(uint32_t in)
92 ConfigUtil::DefaultBoardConfig()
95 memset(&config, 0, sizeof(config));
97 memcpy(config.magic, "BCFG", 4);
100 // Default to maximum fail-safe options.
101 config.flags6 = S2S_CFG_ENABLE_TERMINATOR;
102 config.selectionDelay = 255; // auto
108 ConfigUtil::Default(size_t targetIdx)
110 S2S_TargetCfg config;
111 memset(&config, 0, sizeof(config));
113 config.scsiId = targetIdx;
116 config.scsiId = config.scsiId | S2S_CFG_TARGET_ENABLED;
118 config.deviceType = S2S_CFG_FIXED;
120 // Default to maximum fail-safe options.
121 config.flagsDEPRECATED = 0;
122 config.deviceTypeModifier = 0;
123 config.sdSectorStart = 0;
125 // Default to 2GB. Many systems have trouble with > 2GB disks, and
126 // a few start to complain at 1GB.
127 config.scsiSectors = 4194303; // 2GB - 1 sector
128 config.bytesPerSector = 512;
129 config.sectorsPerTrack = 63;
130 config.headsPerCylinder = 255;
131 memcpy(config.vendor, " codesrc", 8);
132 memcpy(config.prodId, " SCSI2SD", 16);
133 memcpy(config.revision, " 6.0", 4);
134 memcpy(config.serial, "1234567812345678", 16);
136 // Reserved fields, already set to 0
139 // not supported yet.
147 ConfigUtil::fromBytes(const uint8_t* data)
149 S2S_TargetCfg result;
150 memcpy(&result, data, sizeof(S2S_TargetCfg));
151 result.sdSectorStart = toLE32(result.sdSectorStart);
152 result.scsiSectors = toLE32(result.scsiSectors);
153 result.bytesPerSector = toLE16(result.bytesPerSector);
154 result.sectorsPerTrack = toLE16(result.sectorsPerTrack);
155 result.headsPerCylinder = toLE16(result.headsPerCylinder);
161 ConfigUtil::toBytes(const S2S_TargetCfg& _config)
163 S2S_TargetCfg config(_config);
164 config.sdSectorStart = fromLE32(config.sdSectorStart);
165 config.scsiSectors = fromLE32(config.scsiSectors);
166 config.bytesPerSector = fromLE16(config.bytesPerSector);
167 config.sectorsPerTrack = fromLE16(config.sectorsPerTrack);
168 config.headsPerCylinder = fromLE16(config.headsPerCylinder);
170 const uint8_t* begin = reinterpret_cast<const uint8_t*>(&config);
172 return std::vector<uint8_t>(begin, begin + sizeof(config));
176 ConfigUtil::boardConfigFromBytes(const uint8_t* data)
179 memcpy(&result, data, sizeof(S2S_BoardCfg));
181 if (memcmp("BCFG", result.magic, 4))
183 return DefaultBoardConfig();
191 ConfigUtil::boardConfigToBytes(const S2S_BoardCfg& _config)
193 S2S_BoardCfg config(_config);
195 memcpy(config.magic, "BCFG", 4);
196 const uint8_t* begin = reinterpret_cast<const uint8_t*>(&config);
197 return std::vector<uint8_t>(begin, begin + sizeof(config));
201 ConfigUtil::toXML(const S2S_TargetCfg& config)
206 "<SCSITarget id=\"" <<
207 static_cast<int>(config.scsiId & S2S_CFG_TARGET_ID_BITS) << "\">\n" <<
210 (config.scsiId & S2S_CFG_TARGET_ENABLED ? "true" : "false") <<
214 " <!-- ********************************************************\n" <<
215 " Space separated list. Available options:\n" <<
216 " apple\t\tReturns Apple-specific mode pages\n" <<
217 " omti\t\tOMTI host non-standard link control\n" <<
218 " xebec\t\tXEBEC ignore step options in control byte\n" <<
219 " ********************************************************* -->\n" <<
221 if (config.quirks == S2S_CFG_QUIRKS_APPLE)
225 else if (config.quirks == S2S_CFG_QUIRKS_OMTI)
229 else if (config.quirks == S2S_CFG_QUIRKS_XEBEC)
233 else if (config.quirks == S2S_CFG_QUIRKS_VMS)
242 " <!-- ********************************************************\n" <<
243 " 0x0 Fixed hard drive.\n" <<
244 " 0x1 Removable drive.\n" <<
245 " 0x2 Optical drive (ie. CD drive).\n" <<
246 " 0x3 1.44MB Floppy Drive.\n" <<
247 " ********************************************************* -->\n" <<
249 std::hex << static_cast<int>(config.deviceType) <<
253 " <!-- ********************************************************\n" <<
254 " Device type modifier is usually 0x00. Only change this if your\n" <<
255 " OS requires some special value.\n" <<
257 " 0x4C Data General Micropolis disk\n" <<
258 " ********************************************************* -->\n" <<
259 " <deviceTypeModifier>0x" <<
260 std::hex << static_cast<int>(config.deviceTypeModifier) <<
261 "</deviceTypeModifier>\n" <<
264 " <!-- ********************************************************\n" <<
265 " SD card offset, as a sector number (always 512 bytes).\n" <<
266 " ********************************************************* -->\n" <<
267 " <sdSectorStart>" << std::dec << config.sdSectorStart << "</sdSectorStart>\n" <<
269 " <!-- ********************************************************\n" <<
270 " Drive geometry settings.\n" <<
271 " ********************************************************* -->\n" <<
273 " <scsiSectors>" << std::dec << config.scsiSectors << "</scsiSectors>\n" <<
274 " <bytesPerSector>" << std::dec << config.bytesPerSector << "</bytesPerSector>\n" <<
275 " <sectorsPerTrack>" << std::dec << config.sectorsPerTrack<< "</sectorsPerTrack>\n" <<
276 " <headsPerCylinder>" << std::dec << config.headsPerCylinder << "</headsPerCylinder>\n" <<
278 " <!-- ********************************************************\n" <<
279 " Drive identification information. The SCSI2SD doesn't\n" <<
280 " care what these are set to. Use these strings to trick a OS\n" <<
281 " thinking a specific hard drive model is attached.\n" <<
282 " ********************************************************* -->\n" <<
284 " <!-- 8 character vendor string -->\n" <<
285 " <!-- For Apple HD SC Setup/Drive Setup, use ' SEAGATE' -->\n" <<
286 " <vendor>" << std::string(config.vendor, 8) << "</vendor>\n" <<
288 " <!-- 16 character produce identifier -->\n" <<
289 " <!-- For Apple HD SC Setup/Drive Setup, use ' ST225N' -->\n" <<
290 " <prodId>" << std::string(config.prodId, 16) << "</prodId>\n" <<
292 " <!-- 4 character product revision number -->\n" <<
293 " <!-- For Apple HD SC Setup/Drive Setup, use '1.0 ' -->\n" <<
294 " <revision>" << std::string(config.revision, 4) << "</revision>\n" <<
296 " <!-- 16 character serial number -->\n" <<
297 " <serial>" << std::string(config.serial, 16) << "</serial>\n" <<
305 ConfigUtil::toXML(const S2S_BoardCfg& config)
309 s << "<S2S_BoardCfg>\n" <<
311 " <!-- ********************************************************\n" <<
312 " Enable the onboard active terminator.\n"
313 " Both ends of the SCSI chain should be terminated. Disable\n" <<
314 " only if the SCSI2SD is in the middle of a chain with other\n" <<
316 " ********************************************************* -->\n" <<
317 " <enableTerminator>" <<
318 (config.flags6 & S2S_CFG_ENABLE_TERMINATOR ? "true" : "false") <<
319 "</enableTerminator>\n" <<
321 " <unitAttention>" <<
322 (config.flags & S2S_CFG_ENABLE_UNIT_ATTENTION ? "true" : "false") <<
323 "</unitAttention>\n" <<
326 (config.flags & S2S_CFG_ENABLE_PARITY ? "true" : "false") <<
329 " <!-- ********************************************************\n" <<
330 " Only set to true when using with a fast SCSI2 host\n " <<
331 " controller. This can cause problems with older/slower\n" <<
333 " ********************************************************* -->\n" <<
335 (config.flags & S2S_CFG_ENABLE_SCSI2 ? "true" : "false") <<
336 "</enableScsi2>\n" <<
338 " <!-- ********************************************************\n" <<
339 " Respond to very short duration selection attempts. This supports\n" <<
340 " non-standard hardware, but is generally safe to enable.\n" <<
341 " Required for Philips P2000C.\n" <<
342 " ********************************************************* -->\n" <<
344 (config.flags & S2S_CFG_ENABLE_SEL_LATCH? "true" : "false") <<
348 " <!-- ********************************************************\n" <<
349 " Convert luns to IDs. The unit must already be configured to respond\n" <<
350 " on the ID. Allows dual drives to be accessed from a \n" <<
351 " XEBEC S1410 SASI bridge.\n" <<
352 " eg. Configured for dual drives as IDs 0 and 1, but the XEBEC will\n" <<
353 " access the second disk as ID0, lun 1.\n" <<
354 " See ttp://bitsavers.trailing-edge.com/pdf/xebec/104524C_S1410Man_Aug83.pdf\n" <<
355 " ********************************************************* -->\n" <<
357 (config.flags & S2S_CFG_MAP_LUNS_TO_IDS ? "true" : "false") <<
358 "</mapLunsToIds>\n" <<
361 " <!-- ********************************************************\n" <<
362 " Delay (in milliseconds) before responding to a SCSI selection.\n" <<
363 " 255 (auto) sets it to 0 for SCSI2 hosts and 1ms otherwise.\n" <<
364 " Some samplers need this set to 1 manually.\n" <<
365 " ********************************************************* -->\n" <<
366 " <selectionDelay>" << static_cast<int>(config.selectionDelay) << "</selectionDelay>\n" <<
368 " <!-- ********************************************************\n" <<
369 " Startup delay (in seconds) before responding to the SCSI bus \n" <<
370 " after power on. Default = 0.\n" <<
371 " ********************************************************* -->\n" <<
372 " <startupDelay>" << static_cast<int>(config.startupDelay) << "</startupDelay>\n" <<
374 " <!-- ********************************************************\n" <<
375 " Speed limit the SCSI interface. This is the -max- speed the \n" <<
376 " device will run at. The actual spee depends on the capability\n" <<
377 " of the host controller.\n" <<
379 " 1 Async 1.5MB/s\n" <<
380 " 2 Async 3.3MB/s\n" <<
381 " 3 Async 5MB/s\n" <<
383 " 5 Sync 10MB/s\n" <<
384 " ********************************************************* -->\n" <<
385 " <scsiSpeed>" << static_cast<int>(config.scsiSpeed) << "</scsiSpeed>\n" <<
387 " <!-- ********************************************************\n" <<
388 " Enable SD card blind writes, which starts writing to the SD\n"
389 " card before all the SCSI data has been received. Can cause problems\n" <<
390 " with some SCSI hosts\n" <<
391 " ********************************************************* -->\n" <<
393 (config.flags6 & S2S_CFG_ENABLE_BLIND_WRITES ? "true" : "false") <<
394 "</blindWrites>\n" <<
402 static uint64_t parseInt(wxXmlNode* node, uint64_t limit)
404 std::string str(node->GetNodeContent().mb_str());
407 throw std::runtime_error("Empty " + node->GetName());
411 if (str.find("0x") == 0)
413 s << std::hex << str.substr(2);
424 throw std::runtime_error("Invalid value for " + node->GetName());
429 std::stringstream msg;
430 msg << "Invalid value for " << node->GetName() <<
431 " (max=" << limit << ")";
432 throw std::runtime_error(msg.str());
438 parseTarget(wxXmlNode* node)
443 s << node->GetAttribute("id", "7");
445 if (!s) throw std::runtime_error("Could not parse SCSITarget id attr");
447 S2S_TargetCfg result = ConfigUtil::Default(id & 0x7);
449 wxXmlNode *child = node->GetChildren();
452 if (child->GetName() == "enabled")
454 std::string s(child->GetNodeContent().mb_str());
457 result.scsiId |= S2S_CFG_TARGET_ENABLED;
461 result.scsiId = result.scsiId & ~S2S_CFG_TARGET_ENABLED;
464 else if (child->GetName() == "quirks")
466 std::stringstream s(std::string(child->GetNodeContent().mb_str()));
470 if (quirk == "apple")
472 result.quirks |= S2S_CFG_QUIRKS_APPLE;
474 else if (quirk == "omti")
476 result.quirks |= S2S_CFG_QUIRKS_OMTI;
478 else if (quirk == "xebec")
480 result.quirks |= S2S_CFG_QUIRKS_XEBEC;
482 else if (quirk == "vms")
484 result.quirks |= S2S_CFG_QUIRKS_VMS;
488 else if (child->GetName() == "deviceType")
490 result.deviceType = parseInt(child, 0xFF);
492 else if (child->GetName() == "deviceTypeModifier")
494 result.deviceTypeModifier = parseInt(child, 0xFF);
496 else if (child->GetName() == "sdSectorStart")
498 result.sdSectorStart = parseInt(child, 0xFFFFFFFF);
500 else if (child->GetName() == "scsiSectors")
502 result.scsiSectors = parseInt(child, 0xFFFFFFFF);
504 else if (child->GetName() == "bytesPerSector")
506 result.bytesPerSector = parseInt(child, 8192);
508 else if (child->GetName() == "sectorsPerTrack")
510 result.sectorsPerTrack = parseInt(child, 255);
512 else if (child->GetName() == "headsPerCylinder")
514 result.headsPerCylinder = parseInt(child, 255);
516 else if (child->GetName() == "vendor")
518 std::string s(child->GetNodeContent().mb_str());
519 s = s.substr(0, sizeof(result.vendor));
520 memset(result.vendor, ' ', sizeof(result.vendor));
521 memcpy(result.vendor, s.c_str(), s.size());
523 else if (child->GetName() == "prodId")
525 std::string s(child->GetNodeContent().mb_str());
526 s = s.substr(0, sizeof(result.prodId));
527 memset(result.prodId, ' ', sizeof(result.prodId));
528 memcpy(result.prodId, s.c_str(), s.size());
530 else if (child->GetName() == "revision")
532 std::string s(child->GetNodeContent().mb_str());
533 s = s.substr(0, sizeof(result.revision));
534 memset(result.revision, ' ', sizeof(result.revision));
535 memcpy(result.revision, s.c_str(), s.size());
537 else if (child->GetName() == "serial")
539 std::string s(child->GetNodeContent().mb_str());
540 s = s.substr(0, sizeof(result.serial));
541 memset(result.serial, ' ', sizeof(result.serial));
542 memcpy(result.serial, s.c_str(), s.size());
546 child = child->GetNext();
552 parseBoardConfig(wxXmlNode* node)
554 S2S_BoardCfg result = ConfigUtil::DefaultBoardConfig();
556 wxXmlNode *child = node->GetChildren();
559 if (child->GetName() == "selectionDelay")
561 result.selectionDelay = parseInt(child, 255);
563 else if (child->GetName() == "startupDelay")
565 result.startupDelay = parseInt(child, 255);
567 else if (child->GetName() == "unitAttention")
569 std::string s(child->GetNodeContent().mb_str());
572 result.flags |= S2S_CFG_ENABLE_UNIT_ATTENTION;
576 result.flags = result.flags & ~S2S_CFG_ENABLE_UNIT_ATTENTION;
579 else if (child->GetName() == "parity")
581 std::string s(child->GetNodeContent().mb_str());
584 result.flags |= S2S_CFG_ENABLE_PARITY;
588 result.flags = result.flags & ~S2S_CFG_ENABLE_PARITY;
591 else if (child->GetName() == "enableScsi2")
593 std::string s(child->GetNodeContent().mb_str());
596 result.flags |= S2S_CFG_ENABLE_SCSI2;
600 result.flags = result.flags & ~S2S_CFG_ENABLE_SCSI2;
603 else if (child->GetName() == "enableTerminator")
605 std::string s(child->GetNodeContent().mb_str());
608 result.flags6 |= S2S_CFG_ENABLE_TERMINATOR;
612 result.flags6 = result.flags & ~S2S_CFG_ENABLE_TERMINATOR;
615 else if (child->GetName() == "blindWrites")
617 std::string s(child->GetNodeContent().mb_str());
620 result.flags6 |= S2S_CFG_ENABLE_BLIND_WRITES;
624 result.flags6 = result.flags & ~S2S_CFG_ENABLE_BLIND_WRITES;
627 else if (child->GetName() == "selLatch")
629 std::string s(child->GetNodeContent().mb_str());
632 result.flags |= S2S_CFG_ENABLE_SEL_LATCH;
636 result.flags = result.flags & ~S2S_CFG_ENABLE_SEL_LATCH;
639 else if (child->GetName() == "mapLunsToIds")
641 std::string s(child->GetNodeContent().mb_str());
644 result.flags |= S2S_CFG_MAP_LUNS_TO_IDS;
648 result.flags = result.flags & ~S2S_CFG_MAP_LUNS_TO_IDS;
651 else if (child->GetName() == "scsiSpeed")
653 result.scsiSpeed = parseInt(child, S2S_CFG_SPEED_SYNC_10);
655 child = child->GetNext();
661 std::pair<S2S_BoardCfg, std::vector<S2S_TargetCfg>>
662 ConfigUtil::fromXML(const std::string& filename)
665 if (!doc.Load(filename))
667 throw std::runtime_error("Could not load XML file");
670 // start processing the XML file
671 if (doc.GetRoot()->GetName() != "SCSI2SD")
673 throw std::runtime_error("Invalid root node, expected <SCSI2SD>");
676 S2S_BoardCfg boardConfig = DefaultBoardConfig();
677 int boardConfigFound = 0;
679 std::vector<S2S_TargetCfg> targets;
680 wxXmlNode *child = doc.GetRoot()->GetChildren();
683 if (child->GetName() == "SCSITarget")
685 targets.push_back(parseTarget(child));
687 else if (child->GetName() == "S2S_BoardCfg")
689 boardConfig = parseBoardConfig(child);
690 boardConfigFound = 1;
692 child = child->GetNext();
695 if (!boardConfigFound && targets.size() > 0)
697 boardConfig.flags = targets[0].flagsDEPRECATED;
699 return make_pair(boardConfig, targets);