EMU EMAX 1/2 fixes.
[SCSI2SD-V6.git] / software / scsi2sd-util / TargetPanel.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 // For compilers that support precompilation, includes "wx/wx.h".
19 #include <wx/wxprec.h>
20 #ifndef WX_PRECOMP
21 #include <wx/wx.h>
22 #endif
23
24 #include <wx/wrapsizer.h>
25
26 #include "ConfigUtil.hh"
27 #include "TargetPanel.hh"
28
29 #include <limits>
30 #include <sstream>
31
32 #include <math.h>
33 #include <string.h>
34
35 using namespace SCSI2SD;
36
37 wxDEFINE_EVENT(SCSI2SD::ConfigChangedEvent, wxCommandEvent);
38
39 namespace
40 {
41         template<typename IntType, class WXCTRL> std::pair<IntType, bool>
42         CtrlGetValue(WXCTRL* ctrl)
43         {
44                 IntType value;
45                 std::stringstream conv;
46                 conv << ctrl->GetValue();
47                 conv >> value;
48                 return std::make_pair(value, static_cast<bool>(conv));
49         }
50
51         void CtrlGetFixedString(wxTextEntry* ctrl, char* dest, size_t len)
52         {
53                 memset(dest, ' ', len);
54                 std::string str(ctrl->GetValue().ToAscii());
55                 // Don't use strncpy - we need to avoid NULL's
56                 memcpy(dest, str.c_str(), std::min(len, str.size()));
57         }
58
59         bool CtrlIsAscii(wxTextEntry* ctrl)
60         {
61                 return ctrl->GetValue().IsAscii();
62         }
63
64 }
65
66 TargetPanel::TargetPanel(wxWindow* parent, const TargetConfig& initialConfig) :
67         wxPanel(parent),
68         myParent(parent),
69         myAutoStartSector(0),
70         myStartSDSectorValidator(new wxIntegerValidator<uint32_t>),
71         mySectorSizeValidator(new wxIntegerValidator<uint16_t>),
72         myNumSectorValidator(new wxIntegerValidator<uint32_t>),
73         mySizeValidator(new wxFloatingPointValidator<float>(2))
74 {
75         wxFlexGridSizer *fgs = new wxFlexGridSizer(13, 3, 9, 25);
76
77         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("")));
78         myEnableCtrl =
79                 new wxCheckBox(
80                         this,
81                         ID_enableCtrl,
82                         wxT("Enable SCSI Target"));
83         fgs->Add(myEnableCtrl);
84         // Set a non-visible string to leave room in the column for future messages
85         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("                                        ")));
86         Bind(wxEVT_CHECKBOX, &TargetPanel::onInput<wxCommandEvent>, this, ID_enableCtrl);
87
88
89         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("SCSI ID")));
90         myScsiIdCtrl =
91                 new wxSpinCtrl
92                         (this,
93                         ID_scsiIdCtrl,
94                         wxEmptyString,
95                         wxDefaultPosition,
96                         wxDefaultSize,
97                         wxSP_WRAP | wxSP_ARROW_KEYS,
98                         0,
99                         7,
100                         0);
101         fgs->Add(myScsiIdCtrl);
102         myScsiIdMsg = new wxStaticText(this, wxID_ANY, wxT(""));
103         fgs->Add(myScsiIdMsg);
104         Bind(wxEVT_SPINCTRL, &TargetPanel::onInput<wxSpinEvent>, this, ID_scsiIdCtrl);
105
106         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Device Type")));
107         wxString deviceTypes[] =
108         {
109                 wxT("Hard Drive"),
110                 wxT("Removable"),
111                 wxT("CDROM"),
112                 wxT("3.5\" Floppy")
113         };
114         myDeviceTypeCtrl =
115                 new wxChoice(
116                         this,
117                         ID_deviceTypeCtrl,
118                         wxDefaultPosition,
119                         wxDefaultSize,
120                         sizeof(deviceTypes) / sizeof(wxString),
121                         deviceTypes
122                         );
123         myDeviceTypeCtrl->SetSelection(0);
124         fgs->Add(myDeviceTypeCtrl);
125         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("")));
126         Bind(wxEVT_CHOICE, &TargetPanel::onInput<wxCommandEvent>, this, ID_deviceTypeCtrl);
127
128         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("")));
129         myParityCtrl =
130                 new wxCheckBox(
131                         this,
132                         ID_parityCtrl,
133                         wxT("Enable Parity"));
134         fgs->Add(myParityCtrl);
135         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("")));
136         Bind(wxEVT_CHECKBOX, &TargetPanel::onInput<wxCommandEvent>, this, ID_parityCtrl);
137
138         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("")));
139         myUnitAttCtrl =
140                 new wxCheckBox(
141                         this,
142                         ID_unitAttCtrl,
143                         wxT("Enable Unit Attention"));
144
145         fgs->Add(myUnitAttCtrl);
146         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("")));
147         Bind(wxEVT_CHECKBOX, &TargetPanel::onInput<wxCommandEvent>, this, ID_unitAttCtrl);
148
149         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("SD card start sector")));
150         wxWrapSizer* startContainer = new wxWrapSizer();
151         myStartSDSectorCtrl =
152                 new wxTextCtrl(
153                         this,
154                         ID_startSDSectorCtrl,
155                         "0",
156                         wxDefaultPosition,
157                         wxDefaultSize,
158                         0,
159                         *myStartSDSectorValidator);
160         myStartSDSectorCtrl->SetToolTip(wxT("Supports multiple SCSI targets "
161                 "on a single memory card. In units of 512-byte sectors."));
162         startContainer->Add(myStartSDSectorCtrl);
163         myAutoStartSectorCtrl =
164                 new wxCheckBox(
165                         this,
166                         ID_autoStartSectorCtrl,
167                         wxT("Auto"));
168         startContainer->Add(myAutoStartSectorCtrl);
169         Bind(wxEVT_CHECKBOX, &TargetPanel::onInput<wxCommandEvent>, this, ID_autoStartSectorCtrl);
170         fgs->Add(startContainer);
171         myStartSDSectorMsg = new wxStaticText(this, wxID_ANY, wxT(""));
172         fgs->Add(myStartSDSectorMsg);
173         Bind(wxEVT_TEXT, &TargetPanel::onInput<wxCommandEvent>, this, ID_startSDSectorCtrl);
174
175
176         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Sector size (bytes)")));
177         mySectorSizeCtrl =
178                 new wxTextCtrl(
179                         this,
180                         ID_sectorSizeCtrl,
181                         "512",
182                         wxDefaultPosition,
183                         wxDefaultSize,
184                         0,
185                         *mySectorSizeValidator);
186         mySectorSizeCtrl->SetToolTip(wxT("Between 64 and 8192. Default of 512 is suitable in most cases."));
187         fgs->Add(mySectorSizeCtrl);
188         mySectorSizeMsg = new wxStaticText(this, wxID_ANY, wxT(""));
189         fgs->Add(mySectorSizeMsg);
190         Bind(wxEVT_TEXT, &TargetPanel::onSizeInput, this, ID_sectorSizeCtrl);
191
192
193         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Sector count")));
194         myNumSectorCtrl =
195                 new wxTextCtrl(
196                         this,
197                         ID_numSectorCtrl,
198                         "",
199                         wxDefaultPosition,
200                         wxDefaultSize,
201                         0,
202                         *myNumSectorValidator);
203         myNumSectorCtrl->SetToolTip(wxT("Number of sectors (device size)"));
204         fgs->Add(myNumSectorCtrl);
205         myNumSectorMsg = new wxStaticText(this, wxID_ANY, wxT(""));
206         fgs->Add(myNumSectorMsg);
207         Bind(wxEVT_TEXT, &TargetPanel::onSizeInput, this, ID_numSectorCtrl);
208
209
210         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Device size")));
211         wxWrapSizer* sizeContainer = new wxWrapSizer();
212         mySizeCtrl =
213                 new wxTextCtrl(
214                         this,
215                         ID_sizeCtrl,
216                         "",
217                         wxDefaultPosition,
218                         wxDefaultSize,
219                         0,
220                         *mySizeValidator);
221         mySizeCtrl->SetToolTip(wxT("Device size"));
222         sizeContainer->Add(mySizeCtrl);
223         wxString units[] = {wxT("KB"), wxT("MB"), wxT("GB")};
224         mySizeUnitCtrl =
225                 new wxChoice(
226                         this,
227                         ID_sizeUnitCtrl,
228                         wxDefaultPosition,
229                         wxDefaultSize,
230                         sizeof(units) / sizeof(wxString),
231                         units
232                         );
233         sizeContainer->Add(mySizeUnitCtrl);
234         fgs->Add(sizeContainer);
235         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("")));
236         Bind(wxEVT_TEXT, &TargetPanel::onSizeInput, this, ID_sizeCtrl);
237         Bind(wxEVT_CHOICE, &TargetPanel::onSizeInput, this, ID_sizeUnitCtrl);
238
239         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Vendor")));
240         myVendorCtrl =
241                 new wxTextCtrl(
242                         this,
243                         ID_vendorCtrl,
244                         wxEmptyString,
245                         wxDefaultPosition,
246                         wxSize(GetCharWidth() * 10, -1));
247         myVendorCtrl->SetMaxLength(8);
248         myVendorCtrl->SetToolTip(wxT("SCSI Vendor string. eg. ' codesrc'"));
249         fgs->Add(myVendorCtrl);
250         myVendorMsg = new wxStaticText(this, wxID_ANY, wxT(""));
251         fgs->Add(myVendorMsg);
252         Bind(wxEVT_TEXT, &TargetPanel::onInput<wxCommandEvent>, this, ID_vendorCtrl);
253
254         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Product ID")));
255         myProductCtrl =
256                 new wxTextCtrl(
257                         this,
258                         ID_productCtrl,
259                         wxEmptyString,
260                         wxDefaultPosition,
261                         wxSize(GetCharWidth() * 17, -1));
262         myProductCtrl->SetMaxLength(18);
263         myProductCtrl->SetToolTip(wxT("SCSI Product ID string. eg. 'SCSI2SD'"));
264         fgs->Add(myProductCtrl);
265         myProductMsg = new wxStaticText(this, wxID_ANY, wxT(""));
266         fgs->Add(myProductMsg);
267         Bind(wxEVT_TEXT, &TargetPanel::onInput<wxCommandEvent>, this, ID_productCtrl);
268
269         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Revision")));
270         myRevisionCtrl =
271                 new wxTextCtrl(
272                         this,
273                         ID_revisionCtrl,
274                         wxEmptyString,
275                         wxDefaultPosition,
276                         wxSize(GetCharWidth() * 6, -1));
277         myRevisionCtrl->SetMaxLength(4);
278         myRevisionCtrl->SetToolTip(wxT("SCSI device revision string. eg. '3.5a'"));
279         fgs->Add(myRevisionCtrl);
280         myRevisionMsg = new wxStaticText(this, wxID_ANY, wxT(""));
281         fgs->Add(myRevisionMsg);
282         Bind(wxEVT_TEXT, &TargetPanel::onInput<wxCommandEvent>, this, ID_revisionCtrl);
283
284         fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Serial number")));
285         mySerialCtrl =
286                 new wxTextCtrl(
287                         this,
288                         ID_serialCtrl,
289                         wxEmptyString,
290                         wxDefaultPosition,
291                         wxSize(GetCharWidth() * 18, -1));
292         mySerialCtrl->SetMaxLength(16);
293         mySerialCtrl->SetToolTip(wxT("SCSI serial number. eg. '13eab5632a'"));
294         fgs->Add(mySerialCtrl);
295         mySerialMsg = new wxStaticText(this, wxID_ANY, wxT(""));
296         fgs->Add(mySerialMsg);
297         Bind(wxEVT_TEXT, &TargetPanel::onInput<wxCommandEvent>, this, ID_serialCtrl);
298
299         wxBoxSizer* hbox = new wxBoxSizer(wxHORIZONTAL);
300         hbox->Add(fgs, 1, wxALL | wxEXPAND, 15);
301         this->SetSizer(hbox);
302         Centre();
303
304
305         setConfig(initialConfig);
306         evaluate();
307 }
308
309 bool
310 TargetPanel::evaluate()
311 {
312         bool valid = true;
313         std::stringstream conv;
314
315         bool enabled = myEnableCtrl->IsChecked();
316         {
317                 myScsiIdCtrl->Enable(enabled);
318                 myDeviceTypeCtrl->Enable(enabled);
319                 myParityCtrl->Enable(enabled);
320                 myUnitAttCtrl->Enable(enabled);
321                 myStartSDSectorCtrl->Enable(enabled && !myAutoStartSectorCtrl->IsChecked());
322                 myAutoStartSectorCtrl->Enable(enabled);
323                 mySectorSizeCtrl->Enable(enabled);
324                 myNumSectorCtrl->Enable(enabled);
325                 mySizeCtrl->Enable(enabled);
326                 mySizeUnitCtrl->Enable(enabled);
327                 myVendorCtrl->Enable(enabled);
328                 myProductCtrl->Enable(enabled);
329                 myRevisionCtrl->Enable(enabled);
330                 mySerialCtrl->Enable(enabled);
331         }
332
333         switch (myDeviceTypeCtrl->GetSelection())
334         {
335         case CONFIG_OPTICAL:
336                 mySectorSizeCtrl->ChangeValue("2048");
337                 mySectorSizeCtrl->Enable(false);
338                 break;
339         case CONFIG_FLOPPY_14MB:
340                 mySectorSizeCtrl->ChangeValue("512");
341                 mySectorSizeCtrl->Enable(false);
342                 myNumSectorCtrl->ChangeValue("2880");
343                 myNumSectorCtrl->Enable(false);
344                 mySizeUnitCtrl->Enable(false);
345                 mySizeCtrl->Enable(false);
346                 break;
347         };
348         evaluateSize();
349
350         if (myAutoStartSectorCtrl->IsChecked())
351         {
352                 std::stringstream ss; ss << myAutoStartSector;
353                 myStartSDSectorCtrl->ChangeValue(ss.str());
354         }
355
356         uint32_t startSDsector;
357         {
358                 conv << myStartSDSectorCtrl->GetValue();
359                 conv >> startSDsector;
360         }
361
362         if (!conv)
363                 // TODO check if it is beyond the current SD card.
364         {
365                 myStartSDSectorMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Number &gt;= 0</span>"));
366                 valid = false;
367         }
368         else
369         {
370                 myStartSDSectorMsg->SetLabelMarkup("");
371         }
372         conv.str(std::string()); conv.clear();
373
374         uint16_t sectorSize(CtrlGetValue<uint16_t>(mySectorSizeCtrl).first);
375         if (sectorSize < 64 || sectorSize > 8192)
376         {
377                 mySectorSizeMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Must be between 64 and 8192</span>"));
378                 valid = false;
379         }
380         else
381         {
382                 mySectorSizeMsg->SetLabelMarkup("");
383         }
384         conv.str(std::string()); conv.clear();
385
386         std::pair<uint32_t, bool> numSectors(CtrlGetValue<uint32_t>(myNumSectorCtrl));
387         if (!numSectors.second ||
388                 numSectors.first == 0 ||
389                 !convertUnitsToSectors().second)
390         {
391                 myNumSectorMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Invalid size</span>"));
392                 valid = false;
393         }
394         else
395         {
396                 myNumSectorMsg->SetLabelMarkup("");
397         }
398
399         if (!CtrlIsAscii(myVendorCtrl))
400         {
401                 myVendorMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Invalid characters</span>"));
402                 valid = false;
403         }
404         else
405         {
406                 myVendorMsg->SetLabelMarkup("");
407         }
408
409         if (!CtrlIsAscii(myProductCtrl))
410         {
411                 myProductMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Invalid characters</span>"));
412                 valid = false;
413         }
414         else
415         {
416                 myProductMsg->SetLabelMarkup("");
417         }
418
419         if (!CtrlIsAscii(myRevisionCtrl))
420         {
421                 myRevisionMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Invalid characters</span>"));
422                 valid = false;
423         }
424         else
425         {
426                 myRevisionMsg->SetLabelMarkup("");
427         }
428
429         if (!CtrlIsAscii(mySerialCtrl))
430         {
431                 mySerialMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Invalid characters</span>"));
432                 valid = false;
433         }
434         else
435         {
436                 mySerialMsg->SetLabelMarkup("");
437         }
438
439         return valid || !enabled;
440 }
441
442 template<typename EvtType> void
443 TargetPanel::onInput(EvtType& event)
444 {
445         wxCommandEvent changeEvent(ConfigChangedEvent);
446         wxPostEvent(myParent, changeEvent);
447 }
448
449 void
450 TargetPanel::onSizeInput(wxCommandEvent& event)
451 {
452         if (event.GetId() != ID_numSectorCtrl)
453         {
454                 std::stringstream ss;
455                 ss << convertUnitsToSectors().first;
456                 myNumSectorCtrl->ChangeValue(ss.str());
457         }
458         evaluateSize();
459         onInput(event); // propagate
460 }
461
462 void
463 TargetPanel::evaluateSize()
464 {
465         uint32_t numSectors;
466         std::stringstream conv;
467         conv << myNumSectorCtrl->GetValue();
468         conv >> numSectors;
469
470         conv.str(""); conv.clear();
471
472         if (conv)
473         {
474                 uint64_t bytes =
475                         uint64_t(numSectors) *
476                                 CtrlGetValue<uint16_t>(mySectorSizeCtrl).first;
477
478                 if (bytes >= 1024 * 1024 * 1024)
479                 {
480                         conv << (bytes / (1024.0 * 1024 * 1024));
481                         mySizeUnitCtrl->SetSelection(UNIT_GB);
482                 }
483                 else if (bytes >= 1024 * 1024)
484                 {
485                         conv << (bytes / (1024.0 * 1024));
486                         mySizeUnitCtrl->SetSelection(UNIT_MB);
487                 }
488                 else
489                 {
490                         conv << (bytes / 1024.0);
491                         mySizeUnitCtrl->SetSelection(UNIT_KB);
492                 }
493                 mySizeCtrl->ChangeValue(conv.str());
494         }
495 }
496
497 std::pair<uint32_t, bool>
498 TargetPanel::convertUnitsToSectors() const
499 {
500         bool valid = true;
501
502         uint64_t multiplier(0);
503         switch (mySizeUnitCtrl->GetSelection())
504         {
505                 case UNIT_KB: multiplier = 1024; break;
506                 case UNIT_MB: multiplier = 1024 * 1024; break;
507                 case UNIT_GB: multiplier = 1024 * 1024 * 1024; break;
508                 default: valid = false;
509         }
510
511         double size;
512         std::stringstream conv;
513         conv << mySizeCtrl->GetValue();
514         conv >> size;
515         valid = valid && conv;
516
517         uint16_t sectorSize = CtrlGetValue<uint16_t>(mySectorSizeCtrl).first;
518         uint64_t sectors = ceil(multiplier * size / sectorSize);
519
520         if (sectors > std::numeric_limits<uint32_t>::max())
521         {
522                 sectors = std::numeric_limits<uint32_t>::max();
523                 valid = false;
524         }
525
526         return std::make_pair(static_cast<uint32_t>(sectors), valid);
527 }
528
529
530 TargetConfig
531 TargetPanel::getConfig() const
532 {
533         TargetConfig config;
534
535         // Try and keep unknown/unused fields as-is to support newer firmware
536         // versions.
537         memcpy(&config, &myConfig, sizeof(config));
538
539         bool valid = true;
540
541         auto scsiId = CtrlGetValue<uint8_t>(myScsiIdCtrl);
542         config.scsiId = scsiId.first & CONFIG_TARGET_ID_BITS;
543         valid = valid && scsiId.second;
544         if (myEnableCtrl->IsChecked())
545         {
546                 config.scsiId = config.scsiId | CONFIG_TARGET_ENABLED;
547         }
548
549         config.deviceType = myDeviceTypeCtrl->GetSelection();
550
551         config.flags =
552                 (myParityCtrl->IsChecked() ? CONFIG_ENABLE_PARITY : 0) |
553                 (myUnitAttCtrl->IsChecked() ? CONFIG_ENABLE_UNIT_ATTENTION : 0);
554
555         auto startSDSector = CtrlGetValue<uint32_t>(myStartSDSectorCtrl);
556         config.sdSectorStart = startSDSector.first;
557         valid = valid && startSDSector.second;
558
559         auto numSectors = CtrlGetValue<uint32_t>(myNumSectorCtrl);
560         config.scsiSectors = numSectors.first;
561         valid = valid && numSectors.second;
562
563         auto sectorSize = CtrlGetValue<uint16_t>(mySectorSizeCtrl);
564         config.bytesPerSector = sectorSize.first;
565         valid = valid && sectorSize.second;
566
567         CtrlGetFixedString(myVendorCtrl, config.vendor, sizeof(config.vendor));
568         CtrlGetFixedString(myProductCtrl, config.prodId, sizeof(config.prodId));
569         CtrlGetFixedString(myRevisionCtrl, config.revision, sizeof(config.revision));
570         CtrlGetFixedString(mySerialCtrl, config.serial, sizeof(config.serial));
571
572         return config;
573 }
574
575 void
576 TargetPanel::setConfig(const TargetConfig& config)
577 {
578         memcpy(&myConfig, &config, sizeof(config));
579
580         myScsiIdCtrl->SetValue(config.scsiId & CONFIG_TARGET_ID_BITS);
581         myEnableCtrl->SetValue(config.scsiId & CONFIG_TARGET_ENABLED);
582
583         myDeviceTypeCtrl->SetSelection(config.deviceType);
584
585         myParityCtrl->SetValue(config.flags & CONFIG_ENABLE_PARITY);
586         myUnitAttCtrl->SetValue(config.flags & CONFIG_ENABLE_UNIT_ATTENTION);
587
588         {
589                 std::stringstream ss; ss << config.sdSectorStart;
590                 myStartSDSectorCtrl->ChangeValue(ss.str());
591                 myAutoStartSectorCtrl->SetValue(0);
592         }
593
594         {
595                 std::stringstream ss; ss << config.scsiSectors;
596                 myNumSectorCtrl->ChangeValue(ss.str());
597         }
598
599         {
600                 std::stringstream ss; ss << config.bytesPerSector;
601                 mySectorSizeCtrl->ChangeValue(ss.str());
602         }
603
604         myVendorCtrl->ChangeValue(std::string(config.vendor, sizeof(config.vendor)));
605         myProductCtrl->ChangeValue(std::string(config.prodId, sizeof(config.prodId)));
606         myRevisionCtrl->ChangeValue(std::string(config.revision, sizeof(config.revision)));
607         mySerialCtrl->ChangeValue(std::string(config.serial, sizeof(config.serial)));
608
609         // Set the size fields based on sector size, and evaluate inputs.
610         wxCommandEvent fakeEvent(wxEVT_NULL, ID_numSectorCtrl);
611         onSizeInput(fakeEvent);
612 }
613
614 bool
615 TargetPanel::isEnabled() const
616 {
617         return myEnableCtrl->IsChecked();
618 }
619
620 uint8_t
621 TargetPanel::getSCSIId() const
622 {
623         return CtrlGetValue<uint8_t>(myScsiIdCtrl).first & CONFIG_TARGET_ID_BITS;
624 }
625
626 std::pair<uint32_t, uint64_t>
627 TargetPanel::getSDSectorRange() const
628 {
629         std::pair<uint32_t, uint64_t> result;
630         result.first = CtrlGetValue<uint32_t>(myStartSDSectorCtrl).first;
631
632         uint32_t numSCSISectors = CtrlGetValue<uint32_t>(myNumSectorCtrl).first;
633         uint16_t scsiSectorSize = CtrlGetValue<uint16_t>(mySectorSizeCtrl).first;
634
635         const int sdSector = 512; // Always 512 for SDHC/SDXC
636         result.second = result.first +
637                 (
638                         ((uint64_t(numSCSISectors) * scsiSectorSize) + (sdSector - 1))
639                                 / sdSector
640                 );
641         return result;
642 }
643
644 void
645 TargetPanel::setDuplicateID(bool duplicate)
646 {
647         if (duplicate)
648         {
649                 myScsiIdMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Duplicate ID</span>"));
650         }
651         else
652         {
653                 myScsiIdMsg->SetLabelMarkup("");
654         }
655 }
656
657 void
658 TargetPanel::setSDSectorOverlap(bool overlap)
659 {
660         if (overlap)
661         {
662                 myStartSDSectorMsg->SetLabelMarkup(wxT("<span foreground='red' weight='bold'>Overlapping data</span>"));
663         }
664         else
665         {
666                 myStartSDSectorMsg->SetLabelMarkup("");
667         }
668 }
669
670 void
671 TargetPanel::setAutoStartSector(uint32_t start)
672 {
673         myAutoStartSector = start;
674 }
675