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