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