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