scsi2sd-util crash on exit fix (again)
[SCSI2SD-V6.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 TargetConfig
86 ConfigUtil::Default(size_t targetIdx)
87 {
88         TargetConfig config;
89         memset(&config, 0, sizeof(config));
90
91         config.scsiId = targetIdx;
92         if (targetIdx == 0)
93         {
94                 config.scsiId = config.scsiId | CONFIG_TARGET_ENABLED;
95         }
96         config.deviceType = CONFIG_FIXED;
97
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;
102
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);
113
114         // Reserved fields, already set to 0
115         // config.reserved
116
117         // not supported yet.
118         // config.vpd
119
120         return config;
121 }
122
123
124 TargetConfig
125 ConfigUtil::fromBytes(const uint8_t* data)
126 {
127         TargetConfig result;
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);
134         return result;
135 }
136
137
138 std::vector<uint8_t>
139 ConfigUtil::toBytes(const TargetConfig& _config)
140 {
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);
147
148         const uint8_t* begin = reinterpret_cast<const uint8_t*>(&config);
149         return std::vector<uint8_t>(begin, begin + sizeof(config));
150 }
151
152 std::string
153 ConfigUtil::toXML(const TargetConfig& config)
154 {
155         std::stringstream s;
156
157         s <<
158                 "<SCSITarget id=\"" <<
159                         static_cast<int>(config.scsiId & CONFIG_TARGET_ID_BITS) << "\">\n" <<
160
161                 "       <enabled>" <<
162                         (config.scsiId & CONFIG_TARGET_ENABLED ? "true" : "false") <<
163                         "</enabled>\n" <<
164
165                 "       <unitAttention>" <<
166                         (config.flags & CONFIG_ENABLE_UNIT_ATTENTION ? "true" : "false") <<
167                         "</unitAttention>\n" <<
168
169                 "       <parity>" <<
170                         (config.flags & CONFIG_ENABLE_PARITY ? "true" : "false") <<
171                         "</parity>\n" <<
172
173                 "\n" <<
174                 "       <!-- ********************************************************\n" <<
175                 "       Space separated list. Available options:\n" <<
176                 "       apple\t\tReturns Apple-specific mode pages\n" <<
177                 "       ********************************************************* -->\n" <<
178                 "       <quirks>" <<
179                         (config.quirks & CONFIG_QUIRKS_APPLE ? "apple" : "") <<
180                         "</quirks>\n" <<
181
182                 "\n\n" <<
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" <<
189                 "       <deviceType>0x" <<
190                                 std::hex << static_cast<int>(config.deviceType) <<
191                         "</deviceType>\n" <<
192
193                 "\n\n" <<
194                 "       <!-- ********************************************************\n" <<
195                 "       Device type modifier is usually 0x00. Only change this if your\n" <<
196                 "       OS requires some special value.\n" <<
197                 "\n" <<
198                 "       0x4C    Data General Micropolis disk\n" <<
199                 "       ********************************************************* -->\n" <<
200                 "       <deviceTypeModifier>0x" <<
201                                 std::hex << static_cast<int>(config.deviceTypeModifier) <<
202                         "</deviceTypeModifier>\n" <<
203
204                 "\n\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" <<
209                 "\n\n" <<
210                 "       <!-- ********************************************************\n" <<
211                 "       Drive geometry settings.\n" <<
212                 "       ********************************************************* -->\n" <<
213                 "\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" <<
218                 "\n\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" <<
224                 "\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" <<
228                 "\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" <<
232                 "\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" <<
236                 "\n" <<
237                 "       <!-- 16 character serial number -->\n" <<
238                 "       <serial>" << std::string(config.serial, 16) << "</serial>\n" <<
239                 "</SCSITarget>\n";
240
241         return s.str();
242 }
243
244 static uint64_t parseInt(wxXmlNode* node, uint64_t limit)
245 {
246         std::string str(node->GetNodeContent().mb_str());
247         if (str.empty())
248         {
249                 throw std::runtime_error("Empty " + node->GetName());
250         }
251
252         std::stringstream s;
253         if (str.find("0x") == 0)
254         {
255                 s << std::hex << str.substr(2);
256         }
257         else
258         {
259                 s << str;
260         }
261
262         uint64_t result;
263         s >> result;
264         if (!s)
265         {
266                 throw std::runtime_error("Invalid value for " + node->GetName());
267         }
268
269         if (result > limit)
270         {
271                 std::stringstream msg;
272                 msg << "Invalid value for " << node->GetName() <<
273                         " (max=" << limit << ")";
274                 throw std::runtime_error(msg.str());
275         }
276         return result;
277 }
278
279 static TargetConfig
280 parseTarget(wxXmlNode* node)
281 {
282         int id;
283         {
284                 std::stringstream s;
285                 s << node->GetAttribute("id", "7");
286                 s >> id;
287                 if (!s) throw std::runtime_error("Could not parse SCSITarget id attr");
288         }
289         TargetConfig result = ConfigUtil::Default(id & 0x7);
290
291         wxXmlNode *child = node->GetChildren();
292         while (child)
293         {
294                 if (child->GetName() == "enabled")
295                 {
296                         std::string s(child->GetNodeContent().mb_str());
297                         if (s == "true")
298                         {
299                                 result.scsiId |= CONFIG_TARGET_ENABLED;
300                         }
301                         else
302                         {
303                                 result.scsiId = result.scsiId & ~CONFIG_TARGET_ENABLED;
304                         }
305                 }
306                 if (child->GetName() == "unitAttention")
307                 {
308                         std::string s(child->GetNodeContent().mb_str());
309                         if (s == "true")
310                         {
311                                 result.flags |= CONFIG_ENABLE_UNIT_ATTENTION;
312                         }
313                         else
314                         {
315                                 result.flags = result.flags & ~CONFIG_ENABLE_UNIT_ATTENTION;
316                         }
317                 }
318                 if (child->GetName() == "parity")
319                 {
320                         std::string s(child->GetNodeContent().mb_str());
321                         if (s == "true")
322                         {
323                                 result.flags |= CONFIG_ENABLE_PARITY;
324                         }
325                         else
326                         {
327                                 result.flags = result.flags & ~CONFIG_ENABLE_PARITY;
328                         }
329                 }
330                 else if (child->GetName() == "quirks")
331                 {
332                         std::stringstream s(std::string(child->GetNodeContent().mb_str()));
333                         std::string quirk;
334                         while (s >> quirk)
335                         {
336                                 if (quirk == "apple")
337                                 {
338                                         result.quirks |= CONFIG_QUIRKS_APPLE;
339                                 }
340                         }
341                 }
342                 else if (child->GetName() == "deviceType")
343                 {
344                         result.deviceType = parseInt(child, 0xFF);
345                 }
346                 else if (child->GetName() == "deviceTypeModifier")
347                 {
348                         result.deviceTypeModifier = parseInt(child, 0xFF);
349                 }
350                 else if (child->GetName() == "sdSectorStart")
351                 {
352                         result.sdSectorStart = parseInt(child, 0xFFFFFFFF);
353                 }
354                 else if (child->GetName() == "scsiSectors")
355                 {
356                         result.scsiSectors = parseInt(child, 0xFFFFFFFF);
357                 }
358                 else if (child->GetName() == "bytesPerSector")
359                 {
360                         result.bytesPerSector = parseInt(child, 8192);
361                 }
362                 else if (child->GetName() == "sectorsPerTrack")
363                 {
364                         result.sectorsPerTrack = parseInt(child, 255);
365                 }
366                 else if (child->GetName() == "headsPerCylinder")
367                 {
368                         result.headsPerCylinder = parseInt(child, 255);
369                 }
370                 else if (child->GetName() == "vendor")
371                 {
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());
376                 }
377                 else if (child->GetName() == "prodId")
378                 {
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());
383                 }
384                 else if (child->GetName() == "revision")
385                 {
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());
390                 }
391                 else if (child->GetName() == "serial")
392                 {
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());
397                 }
398
399                 child = child->GetNext();
400         }
401         return result;
402 }
403
404 std::vector<TargetConfig>
405 ConfigUtil::fromXML(const std::string& filename)
406 {
407         wxXmlDocument doc;
408         if (!doc.Load(filename))
409         {
410                 throw std::runtime_error("Could not load XML file");
411         }
412
413         // start processing the XML file
414         if (doc.GetRoot()->GetName() != "SCSI2SD")
415         {
416                 throw std::runtime_error("Invalid root node, expected <SCSI2SD>");
417         }
418
419         std::vector<TargetConfig> result;
420         wxXmlNode *child = doc.GetRoot()->GetChildren();
421         while (child)
422         {
423                 if (child->GetName() == "SCSITarget")
424                 {
425                         result.push_back(parseTarget(child));
426                 }
427                 child = child->GetNext();
428         }
429         return result;
430 }
431