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