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