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