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