Final tweaks for v4.5
[SCSI2SD-V6.git] / software / scsi2sd-util / scsi2sd-util.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
19 // For compilers that support precompilation, includes "wx/wx.h".
20 #include <wx/wxprec.h>
21 #ifndef WX_PRECOMP
22 #include <wx/wx.h>
23 #endif
24
25 #include <wx/app.h>
26 #include <wx/filedlg.h>
27 #include <wx/filefn.h>
28 #include <wx/filename.h>
29 #include <wx/log.h>
30 #include <wx/notebook.h>
31 #include <wx/progdlg.h>
32 #include <wx/utils.h>
33 #include <wx/wfstream.h>
34 #include <wx/windowptr.h>
35 #include <wx/thread.h>
36 #include <wx/txtstrm.h>
37
38 #include <zipper.hh>
39
40 #include "ConfigUtil.hh"
41 #include "BoardPanel.hh"
42 #include "TargetPanel.hh"
43 #include "SCSI2SD_Bootloader.hh"
44 #include "SCSI2SD_HID.hh"
45 #include "Firmware.hh"
46
47 #include <algorithm>
48 #include <iomanip>
49 #include <vector>
50 #include <set>
51 #include <sstream>
52
53 #if __cplusplus >= 201103L
54 #include <cstdint>
55 #include <memory>
56 using std::shared_ptr;
57 #else
58 #include <stdint.h>
59 #include <tr1/memory>
60 using std::tr1::shared_ptr;
61 #endif
62
63 #define MIN_FIRMWARE_VERSION 0x0400
64
65 using namespace SCSI2SD;
66
67 class ProgressWrapper
68 {
69 public:
70         void setProgressDialog(
71                 const wxWindowPtr<wxGenericProgressDialog>& dlg,
72                 size_t maxRows)
73         {
74                 myProgressDialog = dlg;
75                 myMaxRows = maxRows;
76                 myNumRows = 0;
77         }
78
79         void clearProgressDialog()
80         {
81                 myProgressDialog->Show(false);
82                 myProgressDialog.reset();
83         }
84
85         void update(unsigned char arrayId, unsigned short rowNum)
86         {
87                 if (!myProgressDialog) return;
88
89                 myNumRows++;
90
91                 std::stringstream ss;
92                 ss << "Writing flash array " <<
93                         static_cast<int>(arrayId) << " row " <<
94                         static_cast<int>(rowNum);
95                 wxLogMessage("%s", ss.str());
96                 myProgressDialog->Update(myNumRows, ss.str());
97         }
98
99 private:
100         wxWindowPtr<wxGenericProgressDialog> myProgressDialog;
101         size_t myMaxRows;
102         size_t myNumRows;
103 };
104 static ProgressWrapper TheProgressWrapper;
105
106 extern "C"
107 void ProgressUpdate(unsigned char arrayId, unsigned short rowNum)
108 {
109         TheProgressWrapper.update(arrayId, rowNum);
110 }
111
112 namespace
113 {
114
115 static uint8_t sdCrc7(uint8_t* chr, uint8_t cnt, uint8_t crc)
116 {
117         uint8_t a;
118         for(a = 0; a < cnt; a++)
119         {
120                 uint8_t data = chr[a];
121                 uint8_t i;
122                 for(i = 0; i < 8; i++)
123                 {
124                         crc <<= 1;
125                         if ((data & 0x80) ^ (crc & 0x80))
126                         {
127                                 crc ^= 0x09;
128                         }
129                         data <<= 1;
130                 }
131         }
132         return crc & 0x7F;
133 }
134
135 class TimerLock
136 {
137 public:
138         TimerLock(wxTimer* timer) :
139                 myTimer(timer),
140                 myInterval(myTimer->GetInterval())
141         {
142                 myTimer->Stop();
143         };
144
145         virtual ~TimerLock()
146         {
147                 if (myTimer && myInterval > 0)
148                 {
149                         myTimer->Start(myInterval);
150                 }
151         }
152 private:
153         wxTimer* myTimer;
154         int myInterval;
155 };
156
157 class AppFrame : public wxFrame
158 {
159 public:
160         AppFrame() :
161                 wxFrame(NULL, wxID_ANY, "scsi2sd-util", wxPoint(50, 50), wxSize(600, 650)),
162                 myInitialConfig(false),
163                 myTickCounter(0),
164                 myLastPollTime(0)
165         {
166                 wxMenu *menuFile = new wxMenu();
167                 menuFile->Append(
168                         ID_SaveFile,
169                         _("&Save to file..."),
170                         _("Save settings to local file."));
171                 menuFile->Append(
172                         ID_OpenFile,
173                         _("&Open file..."),
174                         _("Load settings from local file."));
175                 menuFile->AppendSeparator();
176                 menuFile->Append(
177                         ID_ConfigDefaults,
178                         _("Load &Defaults"),
179                         _("Load default configuration options."));
180                 menuFile->Append(
181                         ID_Firmware,
182                         _("&Upgrade Firmware..."),
183                         _("Upgrade or inspect device firmware version."));
184                 menuFile->AppendSeparator();
185                 menuFile->Append(wxID_EXIT);
186
187                 wxMenu *menuWindow= new wxMenu();
188                 menuWindow->Append(
189                         ID_LogWindow,
190                         _("Show &Log"),
191                         _("Show debug log window"));
192
193                 wxMenu *menuDebug = new wxMenu();
194                 mySCSILogChk = menuDebug->AppendCheckItem(
195                         ID_SCSILog,
196                         _("Log SCSI data"),
197                         _("Log SCSI commands"));
198
199                 mySelfTestChk = menuDebug->AppendCheckItem(
200                         ID_SelfTest,
201                         _("SCSI Standalone Self-Test"),
202                         _("SCSI Standalone Self-Test"));
203
204                 wxMenu *menuHelp = new wxMenu();
205                 menuHelp->Append(wxID_ABOUT);
206
207                 wxMenuBar *menuBar = new wxMenuBar();
208                 menuBar->Append( menuFile, _("&File") );
209                 menuBar->Append( menuDebug, _("&Debug") );
210                 menuBar->Append( menuWindow, _("&Window") );
211                 menuBar->Append( menuHelp, _("&Help") );
212                 SetMenuBar( menuBar );
213
214                 CreateStatusBar();
215
216                 {
217                         wxPanel* cfgPanel = new wxPanel(this);
218                         wxFlexGridSizer *fgs = new wxFlexGridSizer(3, 1, 15, 15);
219                         cfgPanel->SetSizer(fgs);
220
221                         // Empty space below menu bar.
222                         fgs->Add(5, 5, wxALL);
223
224                         wxNotebook* tabs = new wxNotebook(cfgPanel, ID_Notebook);
225                         myBoardPanel = new BoardPanel(tabs, ConfigUtil::DefaultBoardConfig());
226                         tabs->AddPage(myBoardPanel, _("General Settings"));
227                         for (int i = 0; i < MAX_SCSI_TARGETS; ++i)
228                         {
229                                 TargetPanel* target =
230                                         new TargetPanel(tabs, ConfigUtil::Default(i));
231                                 myTargets.push_back(target);
232                                 std::stringstream ss;
233                                 ss << "Device " << (i + 1);
234                                 tabs->AddPage(target, ss.str());
235                                 target->Fit();
236                         }
237                         tabs->Fit();
238                         fgs->Add(tabs);
239
240
241                         wxPanel* btnPanel = new wxPanel(cfgPanel);
242                         wxFlexGridSizer *btnFgs = new wxFlexGridSizer(1, 2, 5, 5);
243                         btnPanel->SetSizer(btnFgs);
244                         myLoadButton =
245                                 new wxButton(btnPanel, ID_BtnLoad, _("Load from device"));
246                         btnFgs->Add(myLoadButton);
247                         mySaveButton =
248                                 new wxButton(btnPanel, ID_BtnSave, _("Save to device"));
249                         btnFgs->Add(mySaveButton);
250                         fgs->Add(btnPanel);
251
252                         btnPanel->Fit();
253                         cfgPanel->Fit();
254                 }
255                 //Fit(); // Needed to reduce window size on Windows
256                 FitInside(); // Needed on Linux to prevent status bar overlap
257
258                 myLogWindow = new wxLogWindow(this, _("scsi2sd-util debug log"), true);
259                 myLogWindow->PassMessages(false); // Prevent messagebox popups
260
261                 myTimer = new wxTimer(this, ID_Timer);
262                 myTimer->Start(16); //ms, suitable for scsi debug logging
263         }
264
265 private:
266         wxLogWindow* myLogWindow;
267         BoardPanel* myBoardPanel;
268         std::vector<TargetPanel*> myTargets;
269         wxButton* myLoadButton;
270         wxButton* mySaveButton;
271         wxMenuItem* mySCSILogChk;
272         wxMenuItem* mySelfTestChk;
273         wxTimer* myTimer;
274         shared_ptr<HID> myHID;
275         shared_ptr<Bootloader> myBootloader;
276         bool myInitialConfig;
277
278         uint8_t myTickCounter;
279
280         time_t myLastPollTime;
281
282         void mmLogStatus(const std::string& msg)
283         {
284                 // We set PassMessages to false on our log window to prevent popups, but
285                 // this also prevents wxLogStatus from updating the status bar.
286                 SetStatusText(msg);
287                 wxLogMessage(this, "%s", msg.c_str());
288         }
289
290         void onConfigChanged(wxCommandEvent& event)
291         {
292                 evaluate();
293         }
294
295         void evaluate()
296         {
297                 bool valid = true;
298
299                 // Check for duplicate SCSI IDs
300                 std::set<uint8_t> enabledID;
301
302                 // Check for overlapping SD sectors.
303                 std::vector<std::pair<uint32_t, uint64_t> > sdSectors;
304
305                 bool isTargetEnabled = false; // Need at least one enabled
306                 uint32_t autoStartSector = 0;
307                 for (size_t i = 0; i < myTargets.size(); ++i)
308                 {
309                         myTargets[i]->setAutoStartSector(autoStartSector);
310                         valid = myTargets[i]->evaluate() && valid;
311
312                         if (myTargets[i]->isEnabled())
313                         {
314                                 isTargetEnabled = true;
315                                 uint8_t scsiID = myTargets[i]->getSCSIId();
316                                 if (enabledID.find(scsiID) != enabledID.end())
317                                 {
318                                         myTargets[i]->setDuplicateID(true);
319                                         valid = false;
320                                 }
321                                 else
322                                 {
323                                         enabledID.insert(scsiID);
324                                         myTargets[i]->setDuplicateID(false);
325                                 }
326
327                                 auto sdSectorRange = myTargets[i]->getSDSectorRange();
328                                 for (auto it(sdSectors.begin()); it != sdSectors.end(); ++it)
329                                 {
330                                         if (sdSectorRange.first < it->second &&
331                                                 sdSectorRange.second > it->first)
332                                         {
333                                                 valid = false;
334                                                 myTargets[i]->setSDSectorOverlap(true);
335                                         }
336                                         else
337                                         {
338                                                 myTargets[i]->setSDSectorOverlap(false);
339                                         }
340                                 }
341                                 sdSectors.push_back(sdSectorRange);
342                                 autoStartSector = sdSectorRange.second;
343                         }
344                         else
345                         {
346                                 myTargets[i]->setDuplicateID(false);
347                                 myTargets[i]->setSDSectorOverlap(false);
348                         }
349                 }
350
351                 valid = valid && isTargetEnabled; // Need at least one.
352
353                 mySaveButton->Enable(
354                         valid &&
355                         myHID &&
356                         (myHID->getFirmwareVersion() >= MIN_FIRMWARE_VERSION));
357
358                 myLoadButton->Enable(
359                         myHID &&
360                         (myHID->getFirmwareVersion() >= MIN_FIRMWARE_VERSION));
361         }
362
363
364         enum
365         {
366                 ID_ConfigDefaults = wxID_HIGHEST + 1,
367                 ID_Firmware,
368                 ID_Timer,
369                 ID_Notebook,
370                 ID_BtnLoad,
371                 ID_BtnSave,
372                 ID_LogWindow,
373                 ID_SCSILog,
374                 ID_SelfTest,
375                 ID_SaveFile,
376                 ID_OpenFile
377         };
378
379         void OnID_ConfigDefaults(wxCommandEvent& event)
380         {
381                 myBoardPanel->setConfig(ConfigUtil::DefaultBoardConfig());
382                 for (size_t i = 0; i < myTargets.size(); ++i)
383                 {
384                         myTargets[i]->setConfig(ConfigUtil::Default(i));
385                 }
386         }
387
388         void OnID_SaveFile(wxCommandEvent& event)
389         {
390                 TimerLock lock(myTimer);
391
392
393
394                 wxFileDialog dlg(
395                         this,
396                         "Save config settings",
397                         "",
398                         "",
399                         "XML files (*.xml)|*.xml",
400                         wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
401                 if (dlg.ShowModal() == wxID_CANCEL) return;
402
403                 wxFileOutputStream file(dlg.GetPath());
404                 if (!file.IsOk())
405                 {
406                         wxLogError("Cannot save settings to file '%s'.", dlg.GetPath());
407                         return;
408                 }
409
410                 wxTextOutputStream s(file);
411
412                 s << "<SCSI2SD>\n";
413
414                 s << ConfigUtil::toXML(myBoardPanel->getConfig());
415                 for (size_t i = 0; i < myTargets.size(); ++i)
416                 {
417                         s << ConfigUtil::toXML(myTargets[i]->getConfig());
418                 }
419
420                 s << "</SCSI2SD>\n";
421         }
422
423         void OnID_OpenFile(wxCommandEvent& event)
424         {
425                 TimerLock lock(myTimer);
426
427                 wxFileDialog dlg(
428                         this,
429                         "Load config settings",
430                         "",
431                         "",
432                         "XML files (*.xml)|*.xml",
433                         wxFD_OPEN | wxFD_FILE_MUST_EXIST);
434                 if (dlg.ShowModal() == wxID_CANCEL) return;
435
436                 try
437                 {
438                         std::pair<BoardConfig, std::vector<TargetConfig>> configs(
439                                 ConfigUtil::fromXML(std::string(dlg.GetPath())));
440
441                         myBoardPanel->setConfig(configs.first);
442
443                         size_t i;
444                         for (i = 0; i < configs.second.size() && i < myTargets.size(); ++i)
445                         {
446                                 myTargets[i]->setConfig(configs.second[i]);
447                         }
448
449                         for (; i < myTargets.size(); ++i)
450                         {
451                                 myTargets[i]->setConfig(ConfigUtil::Default(i));
452                         }
453                 }
454                 catch (std::exception& e)
455                 {
456                         wxLogError(
457                                 "Cannot load settings from file '%s'.\n%s",
458                                 dlg.GetPath(),
459                                 e.what());
460
461                         wxMessageBox(
462                                 e.what(),
463                                 "Load error",
464                                 wxOK | wxICON_ERROR);
465                 }
466         }
467
468         void OnID_Firmware(wxCommandEvent& event)
469         {
470                 TimerLock lock(myTimer);
471                 doFirmwareUpdate();
472         }
473
474         void OnID_LogWindow(wxCommandEvent& event)
475         {
476                 myLogWindow->Show();
477         }
478
479         void doFirmwareUpdate()
480         {
481                 wxFileDialog dlg(
482                         this,
483                         "Load firmware file",
484                         "",
485                         "",
486                         "SCSI2SD Firmware files (*.scsi2sd;*.cyacd)|*.cyacd;*.scsi2sd",
487                         wxFD_OPEN | wxFD_FILE_MUST_EXIST);
488                 if (dlg.ShowModal() == wxID_CANCEL) return;
489
490                 std::string filename(dlg.GetPath());
491
492                 wxWindowPtr<wxGenericProgressDialog> progress(
493                         new wxGenericProgressDialog(
494                                 "Searching for bootloader",
495                                 "Searching for bootloader",
496                                 100,
497                                 this,
498                                 wxPD_AUTO_HIDE | wxPD_CAN_ABORT)
499                                 );
500                 mmLogStatus("Searching for bootloader");
501                 while (true)
502                 {
503                         try
504                         {
505                                 if (!myHID) myHID.reset(HID::Open());
506                                 if (myHID)
507                                 {
508                                         mmLogStatus("Resetting SCSI2SD into bootloader");
509
510                                         myHID->enterBootloader();
511                                         myHID.reset();
512                                 }
513
514
515                                 if (!myBootloader)
516                                 {
517                                         myBootloader.reset(Bootloader::Open());
518                                         if (myBootloader)
519                                         {
520                                                 mmLogStatus("Bootloader found");
521                                                 break;
522                                         }
523                                 }
524
525                                 else if (myBootloader)
526                                 {
527                                         // Verify the USB HID connection is valid
528                                         if (!myBootloader->ping())
529                                         {
530                                                 mmLogStatus("Bootloader ping failed");
531                                                 myBootloader.reset();
532                                         }
533                                         else
534                                         {
535                                                 mmLogStatus("Bootloader found");
536                                                 break;
537                                         }
538                                 }
539                         }
540                         catch (std::exception& e)
541                         {
542                                 mmLogStatus(e.what());
543                                 myHID.reset();
544                                 myBootloader.reset();
545                         }
546                         wxMilliSleep(100);
547                         if (!progress->Pulse())
548                         {
549                                 return; // user cancelled.
550                         }
551                 }
552
553                 int totalFlashRows = 0;
554                 std::string tmpFile;
555                 try
556                 {
557                         zipper::ReaderPtr reader(new zipper::FileReader(filename));
558                         zipper::Decompressor decomp(reader);
559                         std::vector<zipper::CompressedFilePtr> files(decomp.getEntries());
560                         for (auto it(files.begin()); it != files.end(); it++)
561                         {
562                                 if (myBootloader->isCorrectFirmware((*it)->getPath()))
563                                 {
564                                         std::stringstream msg;
565                                         msg << "Found firmware entry " << (*it)->getPath() <<
566                                                 " within archive " << filename;
567                                         mmLogStatus(msg.str());
568                                         tmpFile =
569                                                 wxFileName::CreateTempFileName(
570                                                         _("SCSI2SD_Firmware"), static_cast<wxFile*>(NULL)
571                                                         );
572                                         zipper::FileWriter out(tmpFile);
573                                         (*it)->decompress(out);
574                                         msg.clear();
575                                         msg << "Firmware extracted to " << tmpFile;
576                                         mmLogStatus(msg.str());
577                                         break;
578                                 }
579                         }
580
581                         if (tmpFile.empty())
582                         {
583                                 // TODO allow "force" option
584                                 wxMessageBox(
585                                         "Wrong filename",
586                                         "Wrong filename",
587                                         wxOK | wxICON_ERROR);
588                                 return;
589                         }
590
591                         Firmware firmware(tmpFile);
592                         totalFlashRows = firmware.totalFlashRows();
593                 }
594                 catch (std::exception& e)
595                 {
596                         mmLogStatus(e.what());
597                         std::stringstream msg;
598                         msg << "Could not open firmware file: " << e.what();
599                         wxMessageBox(
600                                 msg.str(),
601                                 "Bad file",
602                                 wxOK | wxICON_ERROR);
603                         wxRemoveFile(tmpFile);
604                         return;
605                 }
606
607                 {
608                         wxWindowPtr<wxGenericProgressDialog> progress(
609                                 new wxGenericProgressDialog(
610                                         "Loading firmware",
611                                         "Loading firmware",
612                                         totalFlashRows,
613                                         this,
614                                         wxPD_AUTO_HIDE | wxPD_REMAINING_TIME)
615                                         );
616                         TheProgressWrapper.setProgressDialog(progress, totalFlashRows);
617                 }
618
619                 std::stringstream msg;
620                 msg << "Upgrading firmware from file: " << tmpFile;
621                 mmLogStatus(msg.str());
622
623                 try
624                 {
625                         myBootloader->load(tmpFile, &ProgressUpdate);
626                         TheProgressWrapper.clearProgressDialog();
627
628                         wxMessageBox(
629                                 "Firmware update successful",
630                                 "Firmware OK",
631                                 wxOK);
632                         mmLogStatus("Firmware update successful");
633
634
635                         myHID.reset();
636                         myBootloader.reset();
637                 }
638                 catch (std::exception& e)
639                 {
640                         TheProgressWrapper.clearProgressDialog();
641                         mmLogStatus(e.what());
642                         myHID.reset();
643                         myBootloader.reset();
644
645                         wxMessageBox(
646                                 "Firmware Update Failed",
647                                 e.what(),
648                                 wxOK | wxICON_ERROR);
649
650                         wxRemoveFile(tmpFile);
651                 }
652         }
653
654         void dumpSCSICommand(std::vector<uint8_t> buf)
655         {
656                 std::stringstream msg;
657                 msg << std::hex;
658                 for (size_t i = 0; i < 32 && i < buf.size(); ++i)
659                 {
660                         msg << std::setfill('0') << std::setw(2) <<
661                         static_cast<int>(buf[i]) << ' ';
662                 }
663                 wxLogMessage(this, msg.str().c_str());
664         }
665
666         void logSCSI()
667         {
668                 if (!mySCSILogChk->IsChecked() ||
669                         !myHID)
670                 {
671                         return;
672                 }
673                 try
674                 {
675                         std::vector<uint8_t> info(HID::HID_PACKET_SIZE);
676                         if (myHID->readSCSIDebugInfo(info))
677                         {
678                                 dumpSCSICommand(info);
679                         }
680                 }
681                 catch (std::exception& e)
682                 {
683                         wxLogWarning(this, e.what());
684                         myHID.reset();
685                 }
686         }
687
688         void OnID_Timer(wxTimerEvent& event)
689         {
690                 logSCSI();
691                 time_t now = time(NULL);
692                 if (now == myLastPollTime) return;
693                 myLastPollTime = now;
694
695                 // Check if we are connected to the HID device.
696                 // AND/or bootloader device.
697                 try
698                 {
699                         if (myBootloader)
700                         {
701                                 // Verify the USB HID connection is valid
702                                 if (!myBootloader->ping())
703                                 {
704                                         myBootloader.reset();
705                                 }
706                         }
707
708                         if (!myBootloader)
709                         {
710                                 myBootloader.reset(Bootloader::Open());
711
712                                 if (myBootloader)
713                                 {
714                                         mmLogStatus("SCSI2SD Bootloader Ready");
715                                 }
716                         }
717
718                         int supressLog = 0;
719                         if (myHID && myHID->getFirmwareVersion() < MIN_FIRMWARE_VERSION)
720                         {
721                                 // No method to check connection is still valid.
722                                 // So assume it isn't.
723                                 myHID.reset();
724                                 supressLog = 1;
725                         }
726                         else if (myHID && !myHID->ping())
727                         {
728                                 // Verify the USB HID connection is valid
729                                 myHID.reset();
730                         }
731
732                         if (!myHID)
733                         {
734                                 myHID.reset(HID::Open());
735                                 if (myHID)
736                                 {
737                                         if (myHID->getFirmwareVersion() < MIN_FIRMWARE_VERSION)
738                                         {
739                                                 if (!supressLog)
740                                                 {
741                                                         // Oh dear, old firmware
742                                                         std::stringstream msg;
743                                                         msg << "Firmware update required. Version " <<
744                                                                 myHID->getFirmwareVersionStr();
745                                                         mmLogStatus(msg.str());
746                                                 }
747                                         }
748                                         else
749                                         {
750                                                 std::stringstream msg;
751                                                 msg << "SCSI2SD Ready, firmware version " <<
752                                                         myHID->getFirmwareVersionStr();
753                                                 mmLogStatus(msg.str());
754
755                                                 std::vector<uint8_t> csd(myHID->getSD_CSD());
756                                                 std::vector<uint8_t> cid(myHID->getSD_CID());
757                                                 std::stringstream sdinfo;
758                                                 sdinfo << "SD Capacity (512-byte sectors): " <<
759                                                         myHID->getSDCapacity() << std::endl;
760
761                                                 sdinfo << "SD CSD Register: ";
762                                                 if (sdCrc7(&csd[0], 15, 0) != (csd[15] >> 1))
763                                                 {
764                                                         sdinfo << "BADCRC ";
765                                                 }
766                                                 for (size_t i = 0; i < csd.size(); ++i)
767                                                 {
768                                                         sdinfo <<
769                                                                 std::hex << std::setfill('0') << std::setw(2) <<
770                                                                 static_cast<int>(csd[i]);
771                                                 }
772                                                 sdinfo << std::endl;
773                                                 sdinfo << "SD CID Register: ";
774                                                 if (sdCrc7(&cid[0], 15, 0) != (cid[15] >> 1))
775                                                 {
776                                                         sdinfo << "BADCRC ";
777                                                 }
778                                                 for (size_t i = 0; i < cid.size(); ++i)
779                                                 {
780                                                         sdinfo <<
781                                                                 std::hex << std::setfill('0') << std::setw(2) <<
782                                                                 static_cast<int>(cid[i]);
783                                                 }
784
785                                                 wxLogMessage(this, "%s", sdinfo.str());
786
787                                                 if (mySelfTestChk->IsChecked())
788                                                 {
789                                                         std::stringstream scsiInfo;
790                                                         scsiInfo << "SCSI Self-Test: " <<
791                                                                 (myHID->scsiSelfTest() ? "Passed" : "FAIL");
792                                                         wxLogMessage(this, "%s", scsiInfo.str());
793                                                 }
794
795                                                 if (!myInitialConfig)
796                                                 {
797 /* This doesn't work properly, and causes crashes.
798                                                         wxCommandEvent loadEvent(wxEVT_NULL, ID_BtnLoad);
799                                                         GetEventHandler()->AddPendingEvent(loadEvent);
800 */
801                                                 }
802
803                                         }
804                                 }
805                                 else
806                                 {
807                                         char ticks[] = {'/', '-', '\\', '|'};
808                                         std::stringstream ss;
809                                         ss << "Searching for SCSI2SD device " << ticks[myTickCounter % sizeof(ticks)];
810                                         myTickCounter++;
811                                         SetStatusText(ss.str());
812                                 }
813                         }
814                 }
815                 catch (std::runtime_error& e)
816                 {
817                         std::cerr << e.what() << std::endl;
818                         mmLogStatus(e.what());
819                 }
820
821                 evaluate();
822         }
823
824         void doLoad(wxCommandEvent& event)
825         {
826                 TimerLock lock(myTimer);
827                 if (!myHID) return;
828
829                 mmLogStatus("Loading configuration");
830
831                 wxWindowPtr<wxGenericProgressDialog> progress(
832                         new wxGenericProgressDialog(
833                                 "Load config settings",
834                                 "Loading config settings",
835                                 100,
836                                 this,
837                                 wxPD_CAN_ABORT | wxPD_REMAINING_TIME)
838                                 );
839
840                 int currentProgress = 0;
841                 int totalProgress = myTargets.size() * SCSI_CONFIG_ROWS + 1;
842
843                 // Read board config first.
844                 std::vector<uint8_t> boardCfgFlashData;
845                 int flashRow = SCSI_CONFIG_BOARD_ROW;
846                 {
847                         std::stringstream ss;
848                         ss << "Reading flash array " << SCSI_CONFIG_ARRAY <<
849                                 " row " << flashRow;
850                         mmLogStatus(ss.str());
851                         currentProgress += 1;
852
853                         if (!progress->Update(
854                                         (100 * currentProgress) / totalProgress,
855                                         ss.str()
856                                         )
857                                 )
858                         {
859                                 goto abort;
860                         }
861
862                         try
863                         {
864                                 myHID->readFlashRow(
865                                         SCSI_CONFIG_ARRAY, flashRow, boardCfgFlashData);
866                                 myBoardPanel->setConfig(
867                                         ConfigUtil::boardConfigFromBytes(&boardCfgFlashData[0]));
868                         }
869                         catch (std::runtime_error& e)
870                         {
871                                 mmLogStatus(e.what());
872                                 goto err;
873                         }
874                 }
875
876                 flashRow = SCSI_CONFIG_0_ROW;
877                 for (size_t i = 0;
878                         i < myTargets.size();
879                         ++i, flashRow += SCSI_CONFIG_ROWS)
880                 {
881                         std::vector<uint8_t> raw(sizeof(TargetConfig));
882
883                         for (size_t j = 0; j < SCSI_CONFIG_ROWS; ++j)
884                         {
885                                 std::stringstream ss;
886                                 ss << "Reading flash array " << SCSI_CONFIG_ARRAY <<
887                                         " row " << (flashRow + j);
888                                 mmLogStatus(ss.str());
889                                 currentProgress += 1;
890                                 if (currentProgress == totalProgress)
891                                 {
892                                         ss.str("Load Complete.");
893                                         mmLogStatus("Load Complete.");
894                                 }
895
896                                 if (!progress->Update(
897                                                 (100 * currentProgress) / totalProgress,
898                                                 ss.str()
899                                                 )
900                                         )
901                                 {
902                                         goto abort;
903                                 }
904
905                                 std::vector<uint8_t> flashData;
906
907                                 try
908                                 {
909                                         myHID->readFlashRow(
910                                                 SCSI_CONFIG_ARRAY, flashRow + j, flashData);
911
912                                 }
913                                 catch (std::runtime_error& e)
914                                 {
915                                         mmLogStatus(e.what());
916                                         goto err;
917                                 }
918
919                                 std::copy(
920                                         flashData.begin(),
921                                         flashData.end(),
922                                         &raw[j * SCSI_CONFIG_ROW_SIZE]);
923                         }
924                         myTargets[i]->setConfig(ConfigUtil::fromBytes(&raw[0]));
925                 }
926
927                 // Support old boards without board config set
928                 if (memcmp(&boardCfgFlashData[0], "BCFG", 4)) {
929                         BoardConfig defCfg = ConfigUtil::DefaultBoardConfig();
930                         defCfg.flags = myTargets[0]->getConfig().flagsDEPRECATED;
931                         myBoardPanel->setConfig(defCfg);
932                 }
933
934                 myInitialConfig = true;
935                 goto out;
936
937         err:
938                 mmLogStatus("Load failed");
939                 progress->Update(100, "Load failed");
940                 goto out;
941
942         abort:
943                 mmLogStatus("Load Aborted");
944
945         out:
946                 return;
947         }
948
949         void doSave(wxCommandEvent& event)
950         {
951                 TimerLock lock(myTimer);
952                 if (!myHID) return;
953
954                 mmLogStatus("Saving configuration");
955
956                 wxWindowPtr<wxGenericProgressDialog> progress(
957                         new wxGenericProgressDialog(
958                                 "Save config settings",
959                                 "Saving config settings",
960                                 100,
961                                 this,
962                                 wxPD_CAN_ABORT | wxPD_REMAINING_TIME)
963                                 );
964
965
966                 int currentProgress = 0;
967                 int totalProgress = myTargets.size() * SCSI_CONFIG_ROWS + 1;
968
969                 // Write board config first.
970                 int flashRow = SCSI_CONFIG_BOARD_ROW;
971                 {
972                         std::stringstream ss;
973                         ss << "Programming flash array " << SCSI_CONFIG_ARRAY <<
974                                 " row " << flashRow;
975                         mmLogStatus(ss.str());
976                         currentProgress += 1;
977
978                         if (!progress->Update(
979                                         (100 * currentProgress) / totalProgress,
980                                         ss.str()
981                                         )
982                                 )
983                         {
984                                 goto abort;
985                         }
986
987                         std::vector<uint8_t> flashData =
988                                 ConfigUtil::boardConfigToBytes(myBoardPanel->getConfig());
989                         try
990                         {
991                                 myHID->writeFlashRow(
992                                         SCSI_CONFIG_ARRAY, flashRow, flashData);
993                         }
994                         catch (std::runtime_error& e)
995                         {
996                                 mmLogStatus(e.what());
997                                 goto err;
998                         }
999                 }
1000
1001                 flashRow = SCSI_CONFIG_0_ROW;
1002                 for (size_t i = 0;
1003                         i < myTargets.size();
1004                         ++i, flashRow += SCSI_CONFIG_ROWS)
1005                 {
1006                         TargetConfig config(myTargets[i]->getConfig());
1007                         std::vector<uint8_t> raw(ConfigUtil::toBytes(config));
1008
1009                         for (size_t j = 0; j < SCSI_CONFIG_ROWS; ++j)
1010                         {
1011                                 std::stringstream ss;
1012                                 ss << "Programming flash array " << SCSI_CONFIG_ARRAY <<
1013                                         " row " << (flashRow + j);
1014                                 mmLogStatus(ss.str());
1015                                 currentProgress += 1;
1016
1017                                 if (currentProgress == totalProgress)
1018                                 {
1019                                         ss.str("Save Complete.");
1020                                         mmLogStatus("Save Complete.");
1021                                 }
1022                                 if (!progress->Update(
1023                                                 (100 * currentProgress) / totalProgress,
1024                                                 ss.str()
1025                                                 )
1026                                         )
1027                                 {
1028                                         goto abort;
1029                                 }
1030
1031                                 std::vector<uint8_t> flashData(SCSI_CONFIG_ROW_SIZE, 0);
1032                                 std::copy(
1033                                         &raw[j * SCSI_CONFIG_ROW_SIZE],
1034                                         &raw[(1+j) * SCSI_CONFIG_ROW_SIZE],
1035                                         flashData.begin());
1036                                 try
1037                                 {
1038                                         myHID->writeFlashRow(
1039                                                 SCSI_CONFIG_ARRAY, flashRow + j, flashData);
1040                                 }
1041                                 catch (std::runtime_error& e)
1042                                 {
1043                                         mmLogStatus(e.what());
1044                                         goto err;
1045                                 }
1046                         }
1047                 }
1048
1049                 // Reboot so new settings take effect.
1050                 myHID->enterBootloader();
1051                 myHID.reset();
1052
1053
1054                 goto out;
1055
1056         err:
1057                 mmLogStatus("Save failed");
1058                 progress->Update(100, "Save failed");
1059                 goto out;
1060
1061         abort:
1062                 mmLogStatus("Save Aborted");
1063
1064         out:
1065                 return;
1066         }
1067
1068         // Note: Don't confuse this with the wxApp::OnExit virtual method
1069         void OnExitEvt(wxCommandEvent& event);
1070
1071         void OnCloseEvt(wxCloseEvent& event);
1072
1073         void OnAbout(wxCommandEvent& event)
1074         {
1075                 wxMessageBox(
1076                         "SCSI2SD (scsi2sd-util)\n"
1077                         "Copyright (C) 2014 Michael McMaster <michael@codesrc.com>\n"
1078                         "\n"
1079 "This program is free software: you can redistribute it and/or modify\n"
1080 "it under the terms of the GNU General Public License as published by\n"
1081 "the Free Software Foundation, either version 3 of the License, or\n"
1082 "(at your option) any later version.\n"
1083 "\n"
1084 "This program is distributed in the hope that it will be useful,\n"
1085 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1086 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
1087 "GNU General Public License for more details.\n"
1088 "\n"
1089 "You should have received a copy of the GNU General Public License\n"
1090 "along with this program.  If not, see <http://www.gnu.org/licenses/>.\n",
1091
1092                         "About scsi2sd-util", wxOK | wxICON_INFORMATION );
1093         }
1094
1095         wxDECLARE_EVENT_TABLE();
1096 };
1097
1098 wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
1099         EVT_MENU(AppFrame::ID_ConfigDefaults, AppFrame::OnID_ConfigDefaults)
1100         EVT_MENU(AppFrame::ID_Firmware, AppFrame::OnID_Firmware)
1101         EVT_MENU(AppFrame::ID_LogWindow, AppFrame::OnID_LogWindow)
1102         EVT_MENU(AppFrame::ID_SaveFile, AppFrame::OnID_SaveFile)
1103         EVT_MENU(AppFrame::ID_OpenFile, AppFrame::OnID_OpenFile)
1104         EVT_MENU(wxID_EXIT, AppFrame::OnExitEvt)
1105         EVT_MENU(wxID_ABOUT, AppFrame::OnAbout)
1106
1107         EVT_TIMER(AppFrame::ID_Timer, AppFrame::OnID_Timer)
1108
1109         EVT_COMMAND(wxID_ANY, ConfigChangedEvent, AppFrame::onConfigChanged)
1110
1111         EVT_BUTTON(ID_BtnSave, AppFrame::doSave)
1112         EVT_BUTTON(ID_BtnLoad, AppFrame::doLoad)
1113
1114         EVT_CLOSE(AppFrame::OnCloseEvt)
1115
1116 wxEND_EVENT_TABLE()
1117
1118
1119
1120 class App : public wxApp
1121 {
1122 public:
1123         virtual bool OnInit()
1124         {
1125                 AppFrame* frame = new AppFrame();
1126                 frame->Show(true);
1127                 SetTopWindow(frame);
1128                 return true;
1129         }
1130 };
1131 } // namespace
1132
1133 // Main Method
1134 wxIMPLEMENT_APP(App);
1135
1136 void
1137 AppFrame::OnExitEvt(wxCommandEvent& event)
1138 {
1139         wxGetApp().ExitMainLoop();
1140 }
1141
1142 void
1143 AppFrame::OnCloseEvt(wxCloseEvent& event)
1144 {
1145         wxGetApp().ExitMainLoop();
1146 }
1147