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