ConfigUtil.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. // Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
  2. //
  3. // This file is part of SCSI2SD.
  4. //
  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.
  9. //
  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.
  14. //
  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/>.
  17. #include "ConfigUtil.hh"
  18. #include <limits>
  19. #include <sstream>
  20. #include <stdexcept>
  21. #include <string.h>
  22. #include <wx/xml/xml.h>
  23. using namespace SCSI2SD;
  24. namespace
  25. {
  26. // Endian conversion routines.
  27. // The Cortex-M3 inside the Cypress PSoC 5LP is a
  28. // little-endian device.
  29. bool isHostLE()
  30. {
  31. union
  32. {
  33. int i;
  34. char c[sizeof(int)];
  35. } x;
  36. x.i = 1;
  37. return (x.c[0] == 1);
  38. }
  39. uint16_t toLE16(uint16_t in)
  40. {
  41. if (isHostLE())
  42. {
  43. return in;
  44. }
  45. else
  46. {
  47. return (in >> 8) | (in << 8);
  48. }
  49. }
  50. uint16_t fromLE16(uint16_t in)
  51. {
  52. return toLE16(in);
  53. }
  54. uint32_t toLE32(uint32_t in)
  55. {
  56. if (isHostLE())
  57. {
  58. return in;
  59. }
  60. else
  61. {
  62. return (in >> 24) |
  63. ((in >> 8) & 0xff00) |
  64. ((in << 8) & 0xff0000) |
  65. (in << 24);
  66. }
  67. }
  68. uint32_t fromLE32(uint32_t in)
  69. {
  70. return toLE32(in);
  71. }
  72. }
  73. BoardConfig
  74. ConfigUtil::DefaultBoardConfig()
  75. {
  76. BoardConfig config;
  77. memset(&config, 0, sizeof(config));
  78. memcpy(config.magic, "BCFG", 4);
  79. // Default to maximum fail-safe options.
  80. config.flags = 0;
  81. config.selectionDelay = 255; // auto
  82. return config;
  83. }
  84. TargetConfig
  85. ConfigUtil::Default(size_t targetIdx)
  86. {
  87. TargetConfig config;
  88. memset(&config, 0, sizeof(config));
  89. config.scsiId = targetIdx;
  90. if (targetIdx == 0)
  91. {
  92. config.scsiId = config.scsiId | CONFIG_TARGET_ENABLED;
  93. }
  94. config.deviceType = CONFIG_FIXED;
  95. // Default to maximum fail-safe options.
  96. config.flagsDEPRECATED = 0;
  97. config.deviceTypeModifier = 0;
  98. config.sdSectorStart = 0;
  99. // Default to 2GB. Many systems have trouble with > 2GB disks, and
  100. // a few start to complain at 1GB.
  101. config.scsiSectors = 4194303; // 2GB - 1 sector
  102. config.bytesPerSector = 512;
  103. config.sectorsPerTrack = 63;
  104. config.headsPerCylinder = 255;
  105. memcpy(config.vendor, " codesrc", 8);
  106. memcpy(config.prodId, " SCSI2SD", 16);
  107. memcpy(config.revision, " 4.2", 4);
  108. memcpy(config.serial, "1234567812345678", 16);
  109. // Reserved fields, already set to 0
  110. // config.reserved
  111. // not supported yet.
  112. // config.vpd
  113. return config;
  114. }
  115. TargetConfig
  116. ConfigUtil::fromBytes(const uint8_t* data)
  117. {
  118. TargetConfig result;
  119. memcpy(&result, data, sizeof(TargetConfig));
  120. result.sdSectorStart = toLE32(result.sdSectorStart);
  121. result.scsiSectors = toLE32(result.scsiSectors);
  122. result.bytesPerSector = toLE16(result.bytesPerSector);
  123. result.sectorsPerTrack = toLE16(result.sectorsPerTrack);
  124. result.headsPerCylinder = toLE16(result.headsPerCylinder);
  125. return result;
  126. }
  127. std::vector<uint8_t>
  128. ConfigUtil::toBytes(const TargetConfig& _config)
  129. {
  130. TargetConfig config(_config);
  131. config.sdSectorStart = fromLE32(config.sdSectorStart);
  132. config.scsiSectors = fromLE32(config.scsiSectors);
  133. config.bytesPerSector = fromLE16(config.bytesPerSector);
  134. config.sectorsPerTrack = fromLE16(config.sectorsPerTrack);
  135. config.headsPerCylinder = fromLE16(config.headsPerCylinder);
  136. const uint8_t* begin = reinterpret_cast<const uint8_t*>(&config);
  137. return std::vector<uint8_t>(begin, begin + sizeof(config));
  138. }
  139. BoardConfig
  140. ConfigUtil::boardConfigFromBytes(const uint8_t* data)
  141. {
  142. BoardConfig result;
  143. memcpy(&result, data, sizeof(BoardConfig));
  144. if (memcmp("BCFG", result.magic, 4))
  145. {
  146. return DefaultBoardConfig();
  147. }
  148. return result;
  149. }
  150. std::vector<uint8_t>
  151. ConfigUtil::boardConfigToBytes(const BoardConfig& _config)
  152. {
  153. BoardConfig config(_config);
  154. memcpy(config.magic, "BCFG", 4);
  155. const uint8_t* begin = reinterpret_cast<const uint8_t*>(&config);
  156. return std::vector<uint8_t>(begin, begin + sizeof(config));
  157. }
  158. std::string
  159. ConfigUtil::toXML(const TargetConfig& config)
  160. {
  161. std::stringstream s;
  162. s <<
  163. "<SCSITarget id=\"" <<
  164. static_cast<int>(config.scsiId & CONFIG_TARGET_ID_BITS) << "\">\n" <<
  165. " <enabled>" <<
  166. (config.scsiId & CONFIG_TARGET_ENABLED ? "true" : "false") <<
  167. "</enabled>\n" <<
  168. "\n" <<
  169. " <!-- ********************************************************\n" <<
  170. " Space separated list. Available options:\n" <<
  171. " apple\t\tReturns Apple-specific mode pages\n" <<
  172. " ********************************************************* -->\n" <<
  173. " <quirks>" <<
  174. (config.quirks & CONFIG_QUIRKS_APPLE ? "apple" : "") <<
  175. "</quirks>\n" <<
  176. "\n\n" <<
  177. " <!-- ********************************************************\n" <<
  178. " 0x0 Fixed hard drive.\n" <<
  179. " 0x1 Removable drive.\n" <<
  180. " 0x2 Optical drive (ie. CD drive).\n" <<
  181. " 0x3 1.44MB Floppy Drive.\n" <<
  182. " ********************************************************* -->\n" <<
  183. " <deviceType>0x" <<
  184. std::hex << static_cast<int>(config.deviceType) <<
  185. "</deviceType>\n" <<
  186. "\n\n" <<
  187. " <!-- ********************************************************\n" <<
  188. " Device type modifier is usually 0x00. Only change this if your\n" <<
  189. " OS requires some special value.\n" <<
  190. "\n" <<
  191. " 0x4C Data General Micropolis disk\n" <<
  192. " ********************************************************* -->\n" <<
  193. " <deviceTypeModifier>0x" <<
  194. std::hex << static_cast<int>(config.deviceTypeModifier) <<
  195. "</deviceTypeModifier>\n" <<
  196. "\n\n" <<
  197. " <!-- ********************************************************\n" <<
  198. " SD card offset, as a sector number (always 512 bytes).\n" <<
  199. " ********************************************************* -->\n" <<
  200. " <sdSectorStart>" << std::dec << config.sdSectorStart << "</sdSectorStart>\n" <<
  201. "\n\n" <<
  202. " <!-- ********************************************************\n" <<
  203. " Drive geometry settings.\n" <<
  204. " ********************************************************* -->\n" <<
  205. "\n"
  206. " <scsiSectors>" << std::dec << config.scsiSectors << "</scsiSectors>\n" <<
  207. " <bytesPerSector>" << std::dec << config.bytesPerSector << "</bytesPerSector>\n" <<
  208. " <sectorsPerTrack>" << std::dec << config.sectorsPerTrack<< "</sectorsPerTrack>\n" <<
  209. " <headsPerCylinder>" << std::dec << config.headsPerCylinder << "</headsPerCylinder>\n" <<
  210. "\n\n" <<
  211. " <!-- ********************************************************\n" <<
  212. " Drive identification information. The SCSI2SD doesn't\n" <<
  213. " care what these are set to. Use these strings to trick a OS\n" <<
  214. " thinking a specific hard drive model is attached.\n" <<
  215. " ********************************************************* -->\n" <<
  216. "\n"
  217. " <!-- 8 character vendor string -->\n" <<
  218. " <!-- For Apple HD SC Setup/Drive Setup, use ' SEAGATE' -->\n" <<
  219. " <vendor>" << std::string(config.vendor, 8) << "</vendor>\n" <<
  220. "\n" <<
  221. " <!-- 16 character produce identifier -->\n" <<
  222. " <!-- For Apple HD SC Setup/Drive Setup, use ' ST225N' -->\n" <<
  223. " <prodId>" << std::string(config.prodId, 16) << "</prodId>\n" <<
  224. "\n" <<
  225. " <!-- 4 character product revision number -->\n" <<
  226. " <!-- For Apple HD SC Setup/Drive Setup, use '1.0 ' -->\n" <<
  227. " <revision>" << std::string(config.revision, 4) << "</revision>\n" <<
  228. "\n" <<
  229. " <!-- 16 character serial number -->\n" <<
  230. " <serial>" << std::string(config.serial, 16) << "</serial>\n" <<
  231. "</SCSITarget>\n";
  232. return s.str();
  233. }
  234. std::string
  235. ConfigUtil::toXML(const BoardConfig& config)
  236. {
  237. std::stringstream s;
  238. s << "<BoardConfig>\n" <<
  239. " <unitAttention>" <<
  240. (config.flags & CONFIG_ENABLE_UNIT_ATTENTION ? "true" : "false") <<
  241. "</unitAttention>\n" <<
  242. " <parity>" <<
  243. (config.flags & CONFIG_ENABLE_PARITY ? "true" : "false") <<
  244. "</parity>\n" <<
  245. " <!-- ********************************************************\n" <<
  246. " Only set to true when using with a fast SCSI2 host\n " <<
  247. " controller. This can cause problems with older/slower\n" <<
  248. " hardware.\n" <<
  249. " ********************************************************* -->\n" <<
  250. " <enableScsi2>" <<
  251. (config.flags & CONFIG_ENABLE_SCSI2 ? "true" : "false") <<
  252. "</enableScsi2>\n" <<
  253. " <!-- ********************************************************\n" <<
  254. " Setting to 'true' will result in increased performance at the\n" <<
  255. " cost of lower noise immunity.\n" <<
  256. " Only set to true when using short cables with only 1 or two\n" <<
  257. " devices. This should remain off when using external SCSI1 DB25\n" <<
  258. " cables.\n" <<
  259. " ********************************************************* -->\n" <<
  260. " <disableGlitchFilter>" <<
  261. (config.flags & CONFIG_DISABLE_GLITCH ? "true" : "false") <<
  262. "</disableGlitchFilter>\n" <<
  263. " <enableCache>" <<
  264. (config.flags & CONFIG_ENABLE_CACHE ? "true" : "false") <<
  265. "</enableCache>\n" <<
  266. " <enableDisconnect>" <<
  267. (config.flags & CONFIG_ENABLE_DISCONNECT ? "true" : "false") <<
  268. "</enableDisconnect>\n" <<
  269. "</BoardConfig>\n";
  270. return s.str();
  271. }
  272. static uint64_t parseInt(wxXmlNode* node, uint64_t limit)
  273. {
  274. std::string str(node->GetNodeContent().mb_str());
  275. if (str.empty())
  276. {
  277. throw std::runtime_error("Empty " + node->GetName());
  278. }
  279. std::stringstream s;
  280. if (str.find("0x") == 0)
  281. {
  282. s << std::hex << str.substr(2);
  283. }
  284. else
  285. {
  286. s << str;
  287. }
  288. uint64_t result;
  289. s >> result;
  290. if (!s)
  291. {
  292. throw std::runtime_error("Invalid value for " + node->GetName());
  293. }
  294. if (result > limit)
  295. {
  296. std::stringstream msg;
  297. msg << "Invalid value for " << node->GetName() <<
  298. " (max=" << limit << ")";
  299. throw std::runtime_error(msg.str());
  300. }
  301. return result;
  302. }
  303. static TargetConfig
  304. parseTarget(wxXmlNode* node)
  305. {
  306. int id;
  307. {
  308. std::stringstream s;
  309. s << node->GetAttribute("id", "7");
  310. s >> id;
  311. if (!s) throw std::runtime_error("Could not parse SCSITarget id attr");
  312. }
  313. TargetConfig result = ConfigUtil::Default(id & 0x7);
  314. wxXmlNode *child = node->GetChildren();
  315. while (child)
  316. {
  317. if (child->GetName() == "enabled")
  318. {
  319. std::string s(child->GetNodeContent().mb_str());
  320. if (s == "true")
  321. {
  322. result.scsiId |= CONFIG_TARGET_ENABLED;
  323. }
  324. else
  325. {
  326. result.scsiId = result.scsiId & ~CONFIG_TARGET_ENABLED;
  327. }
  328. }
  329. else if (child->GetName() == "quirks")
  330. {
  331. std::stringstream s(std::string(child->GetNodeContent().mb_str()));
  332. std::string quirk;
  333. while (s >> quirk)
  334. {
  335. if (quirk == "apple")
  336. {
  337. result.quirks |= CONFIG_QUIRKS_APPLE;
  338. }
  339. }
  340. }
  341. else if (child->GetName() == "deviceType")
  342. {
  343. result.deviceType = parseInt(child, 0xFF);
  344. }
  345. else if (child->GetName() == "deviceTypeModifier")
  346. {
  347. result.deviceTypeModifier = parseInt(child, 0xFF);
  348. }
  349. else if (child->GetName() == "sdSectorStart")
  350. {
  351. result.sdSectorStart = parseInt(child, 0xFFFFFFFF);
  352. }
  353. else if (child->GetName() == "scsiSectors")
  354. {
  355. result.scsiSectors = parseInt(child, 0xFFFFFFFF);
  356. }
  357. else if (child->GetName() == "bytesPerSector")
  358. {
  359. result.bytesPerSector = parseInt(child, 8192);
  360. }
  361. else if (child->GetName() == "sectorsPerTrack")
  362. {
  363. result.sectorsPerTrack = parseInt(child, 255);
  364. }
  365. else if (child->GetName() == "headsPerCylinder")
  366. {
  367. result.headsPerCylinder = parseInt(child, 255);
  368. }
  369. else if (child->GetName() == "vendor")
  370. {
  371. std::string s(child->GetNodeContent().mb_str());
  372. s = s.substr(0, sizeof(result.vendor));
  373. memset(result.vendor, ' ', sizeof(result.vendor));
  374. memcpy(result.vendor, s.c_str(), s.size());
  375. }
  376. else if (child->GetName() == "prodId")
  377. {
  378. std::string s(child->GetNodeContent().mb_str());
  379. s = s.substr(0, sizeof(result.prodId));
  380. memset(result.prodId, ' ', sizeof(result.prodId));
  381. memcpy(result.prodId, s.c_str(), s.size());
  382. }
  383. else if (child->GetName() == "revision")
  384. {
  385. std::string s(child->GetNodeContent().mb_str());
  386. s = s.substr(0, sizeof(result.revision));
  387. memset(result.revision, ' ', sizeof(result.revision));
  388. memcpy(result.revision, s.c_str(), s.size());
  389. }
  390. else if (child->GetName() == "serial")
  391. {
  392. std::string s(child->GetNodeContent().mb_str());
  393. s = s.substr(0, sizeof(result.serial));
  394. memset(result.serial, ' ', sizeof(result.serial));
  395. memcpy(result.serial, s.c_str(), s.size());
  396. }
  397. child = child->GetNext();
  398. }
  399. return result;
  400. }
  401. static BoardConfig
  402. parseBoardConfig(wxXmlNode* node)
  403. {
  404. BoardConfig result = ConfigUtil::DefaultBoardConfig();
  405. wxXmlNode *child = node->GetChildren();
  406. while (child)
  407. {
  408. if (child->GetName() == "unitAttention")
  409. {
  410. std::string s(child->GetNodeContent().mb_str());
  411. if (s == "true")
  412. {
  413. result.flags |= CONFIG_ENABLE_UNIT_ATTENTION;
  414. }
  415. else
  416. {
  417. result.flags = result.flags & ~CONFIG_ENABLE_UNIT_ATTENTION;
  418. }
  419. }
  420. else if (child->GetName() == "parity")
  421. {
  422. std::string s(child->GetNodeContent().mb_str());
  423. if (s == "true")
  424. {
  425. result.flags |= CONFIG_ENABLE_PARITY;
  426. }
  427. else
  428. {
  429. result.flags = result.flags & ~CONFIG_ENABLE_PARITY;
  430. }
  431. }
  432. else if (child->GetName() == "enableScsi2")
  433. {
  434. std::string s(child->GetNodeContent().mb_str());
  435. if (s == "true")
  436. {
  437. result.flags |= CONFIG_ENABLE_SCSI2;
  438. }
  439. else
  440. {
  441. result.flags = result.flags & ~CONFIG_ENABLE_SCSI2;
  442. }
  443. }
  444. else if (child->GetName() == "disableGlitchFilter")
  445. {
  446. std::string s(child->GetNodeContent().mb_str());
  447. if (s == "true")
  448. {
  449. result.flags |= CONFIG_DISABLE_GLITCH;
  450. }
  451. else
  452. {
  453. result.flags = result.flags & ~CONFIG_DISABLE_GLITCH;
  454. }
  455. }
  456. else if (child->GetName() == "enableCache")
  457. {
  458. std::string s(child->GetNodeContent().mb_str());
  459. if (s == "true")
  460. {
  461. result.flags |= CONFIG_ENABLE_CACHE;
  462. }
  463. else
  464. {
  465. result.flags = result.flags & ~CONFIG_ENABLE_CACHE;
  466. }
  467. }
  468. else if (child->GetName() == "enableDisconnect")
  469. {
  470. std::string s(child->GetNodeContent().mb_str());
  471. if (s == "true")
  472. {
  473. result.flags |= CONFIG_ENABLE_DISCONNECT;
  474. }
  475. else
  476. {
  477. result.flags = result.flags & ~CONFIG_ENABLE_DISCONNECT;
  478. }
  479. }
  480. child = child->GetNext();
  481. }
  482. return result;
  483. }
  484. std::pair<BoardConfig, std::vector<TargetConfig>>
  485. ConfigUtil::fromXML(const std::string& filename)
  486. {
  487. wxXmlDocument doc;
  488. if (!doc.Load(filename))
  489. {
  490. throw std::runtime_error("Could not load XML file");
  491. }
  492. // start processing the XML file
  493. if (doc.GetRoot()->GetName() != "SCSI2SD")
  494. {
  495. throw std::runtime_error("Invalid root node, expected <SCSI2SD>");
  496. }
  497. BoardConfig boardConfig = DefaultBoardConfig();
  498. int boardConfigFound = 0;
  499. std::vector<TargetConfig> targets;
  500. wxXmlNode *child = doc.GetRoot()->GetChildren();
  501. while (child)
  502. {
  503. if (child->GetName() == "SCSITarget")
  504. {
  505. targets.push_back(parseTarget(child));
  506. }
  507. else if (child->GetName() == "BoardConfig")
  508. {
  509. boardConfig = parseBoardConfig(child);
  510. boardConfigFound = 1;
  511. }
  512. child = child->GetNext();
  513. }
  514. if (!boardConfigFound && targets.size() > 0)
  515. {
  516. boardConfig.flags = targets[0].flagsDEPRECATED;
  517. }
  518. return make_pair(boardConfig, targets);
  519. }