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