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