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/xml/xml.h>
29 using namespace SCSI2SD;
33 // Endian conversion routines.
34 // The Cortex-M3 inside the Cypress PSoC 5LP is a
35 // little-endian device.
48 uint16_t toLE16(uint16_t in)
56 return (in >> 8) | (in << 8);
59 uint16_t fromLE16(uint16_t in)
64 uint32_t toLE32(uint32_t in)
73 ((in >> 8) & 0xff00) |
74 ((in << 8) & 0xff0000) |
78 uint32_t fromLE32(uint32_t in)
86 ConfigUtil::Default(size_t targetIdx)
89 memset(&config, 0, sizeof(config));
91 config.scsiId = targetIdx;
94 config.scsiId = config.scsiId | CONFIG_TARGET_ENABLED;
96 config.deviceType = CONFIG_FIXED;
98 // Default to maximum fail-safe options.
99 config.flags = 0;// CONFIG_ENABLE_PARITY | CONFIG_ENABLE_UNIT_ATTENTION;
100 config.deviceTypeModifier = 0;
101 config.sdSectorStart = 0;
103 // Default to 2GB. Many systems have trouble with > 2GB disks, and
104 // a few start to complain at 1GB.
105 config.scsiSectors = 4194303; // 2GB - 1 sector
106 config.bytesPerSector = 512;
107 config.sectorsPerTrack = 63;
108 config.headsPerCylinder = 255;
109 memcpy(config.vendor, " codesrc", 8);
110 memcpy(config.prodId, " SCSI2SD", 16);
111 memcpy(config.revision, " 4.2", 4);
112 memcpy(config.serial, "1234567812345678", 16);
114 // Reserved fields, already set to 0
117 // not supported yet.
125 ConfigUtil::fromBytes(const uint8_t* data)
128 memcpy(&result, data, sizeof(TargetConfig));
129 result.sdSectorStart = toLE32(result.sdSectorStart);
130 result.scsiSectors = toLE32(result.scsiSectors);
131 result.bytesPerSector = toLE16(result.bytesPerSector);
132 result.sectorsPerTrack = toLE16(result.sectorsPerTrack);
133 result.headsPerCylinder = toLE16(result.headsPerCylinder);
139 ConfigUtil::toBytes(const TargetConfig& _config)
141 TargetConfig config(_config);
142 config.sdSectorStart = fromLE32(config.sdSectorStart);
143 config.scsiSectors = fromLE32(config.scsiSectors);
144 config.bytesPerSector = fromLE16(config.bytesPerSector);
145 config.sectorsPerTrack = fromLE16(config.sectorsPerTrack);
146 config.headsPerCylinder = fromLE16(config.headsPerCylinder);
148 const uint8_t* begin = reinterpret_cast<const uint8_t*>(&config);
149 return std::vector<uint8_t>(begin, begin + sizeof(config));
153 ConfigUtil::toXML(const TargetConfig& config)
158 "<SCSITarget id=\"" <<
159 static_cast<int>(config.scsiId & CONFIG_TARGET_ID_BITS) << "\">\n" <<
162 (config.scsiId & CONFIG_TARGET_ENABLED ? "true" : "false") <<
165 " <unitAttention>" <<
166 (config.flags & CONFIG_ENABLE_UNIT_ATTENTION ? "true" : "false") <<
167 "</unitAttention>\n" <<
170 (config.flags & CONFIG_ENABLE_PARITY ? "true" : "false") <<
174 " <!-- ********************************************************\n" <<
175 " Space separated list. Available options:\n" <<
176 " apple\t\tReturns Apple-specific mode pages\n" <<
177 " ********************************************************* -->\n" <<
179 (config.quirks & CONFIG_QUIRKS_APPLE ? "apple" : "") <<
183 " <!-- ********************************************************\n" <<
184 " 0x0 Fixed hard drive.\n" <<
185 " 0x1 Removable drive.\n" <<
186 " 0x2 Optical drive (ie. CD drive).\n" <<
187 " 0x3 1.44MB Floppy Drive.\n" <<
188 " ********************************************************* -->\n" <<
190 std::hex << static_cast<int>(config.deviceType) <<
194 " <!-- ********************************************************\n" <<
195 " Device type modifier is usually 0x00. Only change this if your\n" <<
196 " OS requires some special value.\n" <<
198 " 0x4C Data General Micropolis disk\n" <<
199 " ********************************************************* -->\n" <<
200 " <deviceTypeModifier>0x" <<
201 std::hex << static_cast<int>(config.deviceTypeModifier) <<
202 "</deviceTypeModifier>\n" <<
205 " <!-- ********************************************************\n" <<
206 " SD card offset, as a sector number (always 512 bytes).\n" <<
207 " ********************************************************* -->\n" <<
208 " <sdSectorStart>" << std::dec << config.sdSectorStart << "</sdSectorStart>\n" <<
210 " <!-- ********************************************************\n" <<
211 " Drive geometry settings.\n" <<
212 " ********************************************************* -->\n" <<
214 " <scsiSectors>" << std::dec << config.scsiSectors << "</scsiSectors>\n" <<
215 " <bytesPerSector>" << std::dec << config.bytesPerSector << "</bytesPerSector>\n" <<
216 " <sectorsPerTrack>" << std::dec << config.sectorsPerTrack<< "</sectorsPerTrack>\n" <<
217 " <headsPerCylinder>" << std::dec << config.headsPerCylinder << "</headsPerCylinder>\n" <<
219 " <!-- ********************************************************\n" <<
220 " Drive identification information. The SCSI2SD doesn't\n" <<
221 " care what these are set to. Use these strings to trick a OS\n" <<
222 " thinking a specific hard drive model is attached.\n" <<
223 " ********************************************************* -->\n" <<
225 " <!-- 8 character vendor string -->\n" <<
226 " <!-- For Apple HD SC Setup/Drive Setup, use ' SEAGATE' -->\n" <<
227 " <vendor>" << std::string(config.vendor, 8) << "</vendor>\n" <<
229 " <!-- 16 character produce identifier -->\n" <<
230 " <!-- For Apple HD SC Setup/Drive Setup, use ' ST225N' -->\n" <<
231 " <prodId>" << std::string(config.prodId, 16) << "</prodId>\n" <<
233 " <!-- 4 character product revision number -->\n" <<
234 " <!-- For Apple HD SC Setup/Drive Setup, use '1.0 ' -->\n" <<
235 " <revision>" << std::string(config.revision, 4) << "</revision>\n" <<
237 " <!-- 16 character serial number -->\n" <<
238 " <serial>" << std::string(config.serial, 16) << "</serial>\n" <<
244 static uint64_t parseInt(wxXmlNode* node, uint64_t limit)
246 std::string str(node->GetNodeContent().mb_str());
249 throw std::runtime_error("Empty " + node->GetName());
253 if (str.find("0x") == 0)
255 s << std::hex << str.substr(2);
266 throw std::runtime_error("Invalid value for " + node->GetName());
271 std::stringstream msg;
272 msg << "Invalid value for " << node->GetName() <<
273 " (max=" << limit << ")";
274 throw std::runtime_error(msg.str());
280 parseTarget(wxXmlNode* node)
285 s << node->GetAttribute("id", "7");
287 if (!s) throw std::runtime_error("Could not parse SCSITarget id attr");
289 TargetConfig result = ConfigUtil::Default(id & 0x7);
291 wxXmlNode *child = node->GetChildren();
294 if (child->GetName() == "enabled")
296 std::string s(child->GetNodeContent().mb_str());
299 result.scsiId |= CONFIG_TARGET_ENABLED;
303 result.scsiId = result.scsiId & ~CONFIG_TARGET_ENABLED;
306 if (child->GetName() == "unitAttention")
308 std::string s(child->GetNodeContent().mb_str());
311 result.flags |= CONFIG_ENABLE_UNIT_ATTENTION;
315 result.flags = result.flags & ~CONFIG_ENABLE_UNIT_ATTENTION;
318 if (child->GetName() == "parity")
320 std::string s(child->GetNodeContent().mb_str());
323 result.flags |= CONFIG_ENABLE_PARITY;
327 result.flags = result.flags & ~CONFIG_ENABLE_PARITY;
330 else if (child->GetName() == "quirks")
332 std::stringstream s(std::string(child->GetNodeContent().mb_str()));
336 if (quirk == "apple")
338 result.quirks |= CONFIG_QUIRKS_APPLE;
342 else if (child->GetName() == "deviceType")
344 result.deviceType = parseInt(child, 0xFF);
346 else if (child->GetName() == "deviceTypeModifier")
348 result.deviceTypeModifier = parseInt(child, 0xFF);
350 else if (child->GetName() == "sdSectorStart")
352 result.sdSectorStart = parseInt(child, 0xFFFFFFFF);
354 else if (child->GetName() == "scsiSectors")
356 result.scsiSectors = parseInt(child, 0xFFFFFFFF);
358 else if (child->GetName() == "bytesPerSector")
360 result.bytesPerSector = parseInt(child, 8192);
362 else if (child->GetName() == "sectorsPerTrack")
364 result.sectorsPerTrack = parseInt(child, 255);
366 else if (child->GetName() == "headsPerCylinder")
368 result.headsPerCylinder = parseInt(child, 255);
370 else if (child->GetName() == "vendor")
372 std::string s(child->GetNodeContent().mb_str());
373 s = s.substr(0, sizeof(result.vendor));
374 memset(result.vendor, ' ', sizeof(result.vendor));
375 memcpy(result.vendor, s.c_str(), s.size());
377 else if (child->GetName() == "prodId")
379 std::string s(child->GetNodeContent().mb_str());
380 s = s.substr(0, sizeof(result.prodId));
381 memset(result.prodId, ' ', sizeof(result.prodId));
382 memcpy(result.prodId, s.c_str(), s.size());
384 else if (child->GetName() == "revision")
386 std::string s(child->GetNodeContent().mb_str());
387 s = s.substr(0, sizeof(result.revision));
388 memset(result.revision, ' ', sizeof(result.revision));
389 memcpy(result.revision, s.c_str(), s.size());
391 else if (child->GetName() == "serial")
393 std::string s(child->GetNodeContent().mb_str());
394 s = s.substr(0, sizeof(result.serial));
395 memset(result.serial, ' ', sizeof(result.serial));
396 memcpy(result.serial, s.c_str(), s.size());
399 child = child->GetNext();
404 std::vector<TargetConfig>
405 ConfigUtil::fromXML(const std::string& filename)
408 if (!doc.Load(filename))
410 throw std::runtime_error("Could not load XML file");
413 // start processing the XML file
414 if (doc.GetRoot()->GetName() != "SCSI2SD")
416 throw std::runtime_error("Invalid root node, expected <SCSI2SD>");
419 std::vector<TargetConfig> result;
420 wxXmlNode *child = doc.GetRoot()->GetChildren();
423 if (child->GetName() == "SCSITarget")
425 result.push_back(parseTarget(child));
427 child = child->GetNext();