EMU EMAX 1/2 fixes.
[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                                                         wxCommandEvent loadEvent(wxEVT_NULL, ID_BtnLoad);
785                                                         GetEventHandler()->AddPendingEvent(loadEvent);
786                                                 }
787
788                                         }
789                                 }
790                                 else
791                                 {
792                                         char ticks[] = {'/', '-', '\\', '|'};
793                                         std::stringstream ss;
794                                         ss << "Searching for SCSI2SD device " << ticks[myTickCounter % sizeof(ticks)];
795                                         myTickCounter++;
796                                         SetStatusText(ss.str());
797                                 }
798                         }
799                 }
800                 catch (std::runtime_error& e)
801                 {
802                         std::cerr << e.what() << std::endl;
803                         mmLogStatus(e.what());
804                 }
805
806                 evaluate();
807         }
808
809         void doLoad(wxCommandEvent& event)
810         {
811                 TimerLock lock(myTimer);
812                 if (!myHID) return;
813
814                 mmLogStatus("Loading configuration");
815
816                 wxWindowPtr<wxGenericProgressDialog> progress(
817                         new wxGenericProgressDialog(
818                                 "Load config settings",
819                                 "Loading config settings",
820                                 100,
821                                 this,
822                                 wxPD_CAN_ABORT | wxPD_REMAINING_TIME)
823                                 );
824
825                 int flashRow = SCSI_CONFIG_0_ROW;
826                 int currentProgress = 0;
827                 int totalProgress = myTargets.size() * SCSI_CONFIG_ROWS;
828                 for (size_t i = 0;
829                         i < myTargets.size();
830                         ++i, flashRow += SCSI_CONFIG_ROWS)
831                 {
832                         std::vector<uint8_t> raw(sizeof(TargetConfig));
833
834                         for (size_t j = 0; j < SCSI_CONFIG_ROWS; ++j)
835                         {
836                                 std::stringstream ss;
837                                 ss << "Reading flash array " << SCSI_CONFIG_ARRAY <<
838                                         " row " << (flashRow + j);
839                                 mmLogStatus(ss.str());
840                                 currentProgress += 1;
841                                 if (!progress->Update(
842                                                 (100 * currentProgress) / totalProgress,
843                                                 ss.str()
844                                                 )
845                                         )
846                                 {
847                                         goto abort;
848                                 }
849
850                                 std::vector<uint8_t> flashData;
851
852                                 try
853                                 {
854                                         myHID->readFlashRow(
855                                                 SCSI_CONFIG_ARRAY, flashRow + j, flashData);
856
857                                 }
858                                 catch (std::runtime_error& e)
859                                 {
860                                         mmLogStatus(e.what());
861                                         goto err;
862                                 }
863
864                                 std::copy(
865                                         flashData.begin(),
866                                         flashData.end(),
867                                         &raw[j * SCSI_CONFIG_ROW_SIZE]);
868                         }
869                         myTargets[i]->setConfig(ConfigUtil::fromBytes(&raw[0]));
870                 }
871
872                 myInitialConfig = true;
873                 mmLogStatus("Load Complete");
874                 while (progress->Update(100, "Load Complete"))
875                 {
876                         // Wait for the user to click "Close"
877                         wxMilliSleep(50);
878                 }
879                 goto out;
880
881         err:
882                 mmLogStatus("Load failed");
883                 while (progress->Update(100, "Load failed"))
884                 {
885                         // Wait for the user to click "Close"
886                         wxMilliSleep(50);
887                 }
888                 goto out;
889
890         abort:
891                 mmLogStatus("Load Aborted");
892
893         out:
894                 return;
895         }
896
897         void doSave(wxCommandEvent& event)
898         {
899                 TimerLock lock(myTimer);
900                 if (!myHID) return;
901
902                 mmLogStatus("Saving configuration");
903
904                 wxWindowPtr<wxGenericProgressDialog> progress(
905                         new wxGenericProgressDialog(
906                                 "Save config settings",
907                                 "Saving config settings",
908                                 100,
909                                 this,
910                                 wxPD_CAN_ABORT | wxPD_REMAINING_TIME)
911                                 );
912
913                 int flashRow = SCSI_CONFIG_0_ROW;
914                 int currentProgress = 0;
915                 int totalProgress = myTargets.size() * SCSI_CONFIG_ROWS;
916                 for (size_t i = 0;
917                         i < myTargets.size();
918                         ++i, flashRow += SCSI_CONFIG_ROWS)
919                 {
920                         TargetConfig config(myTargets[i]->getConfig());
921                         std::vector<uint8_t> raw(ConfigUtil::toBytes(config));
922
923                         for (size_t j = 0; j < SCSI_CONFIG_ROWS; ++j)
924                         {
925                                 std::stringstream ss;
926                                 ss << "Programming flash array " << SCSI_CONFIG_ARRAY <<
927                                         " row " << (flashRow + j);
928                                 mmLogStatus(ss.str());
929                                 currentProgress += 1;
930                                 if (!progress->Update(
931                                                 (100 * currentProgress) / totalProgress,
932                                                 ss.str()
933                                                 )
934                                         )
935                                 {
936                                         goto abort;
937                                 }
938
939                                 std::vector<uint8_t> flashData(SCSI_CONFIG_ROW_SIZE, 0);
940                                 std::copy(
941                                         &raw[j * SCSI_CONFIG_ROW_SIZE],
942                                         &raw[(1+j) * SCSI_CONFIG_ROW_SIZE],
943                                         flashData.begin());
944                                 try
945                                 {
946                                         myHID->writeFlashRow(
947                                                 SCSI_CONFIG_ARRAY, flashRow + j, flashData);
948                                 }
949                                 catch (std::runtime_error& e)
950                                 {
951                                         mmLogStatus(e.what());
952                                         goto err;
953                                 }
954                         }
955                 }
956
957                 // Reboot so new settings take effect.
958                 myHID->enterBootloader();
959                 myHID.reset();
960
961                 mmLogStatus("Save Complete");
962                 while (progress->Update(100, "Save Complete"))
963                 {
964                         // Wait for the user to click "Close"
965                         wxMilliSleep(50);
966                 }
967                 goto out;
968
969         err:
970                 mmLogStatus("Save failed");
971                 while (progress->Update(100, "Save failed"))
972                 {
973                         // Wait for the user to click "Close"
974                         wxMilliSleep(50);
975                 }
976                 goto out;
977
978         abort:
979                 mmLogStatus("Save Aborted");
980
981         out:
982                 return;
983         }
984
985         // Note: Don't confuse this with the wxApp::OnExit virtual method
986         void OnExitEvt(wxCommandEvent& event)
987         {
988                 Close(true);
989         }
990
991         void OnAbout(wxCommandEvent& event)
992         {
993                 wxMessageBox(
994                         "SCSI2SD (scsi2sd-util)\n"
995                         "Copyright (C) 2014 Michael McMaster <michael@codesrc.com>\n"
996                         "\n"
997 "This program is free software: you can redistribute it and/or modify\n"
998 "it under the terms of the GNU General Public License as published by\n"
999 "the Free Software Foundation, either version 3 of the License, or\n"
1000 "(at your option) any later version.\n"
1001 "\n"
1002 "This program is distributed in the hope that it will be useful,\n"
1003 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1004 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
1005 "GNU General Public License for more details.\n"
1006 "\n"
1007 "You should have received a copy of the GNU General Public License\n"
1008 "along with this program.  If not, see <http://www.gnu.org/licenses/>.\n",
1009
1010                         "About scsi2sd-util", wxOK | wxICON_INFORMATION );
1011         }
1012
1013         wxDECLARE_EVENT_TABLE();
1014 };
1015
1016 wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
1017         EVT_MENU(AppFrame::ID_ConfigDefaults, AppFrame::OnID_ConfigDefaults)
1018         EVT_MENU(AppFrame::ID_Firmware, AppFrame::OnID_Firmware)
1019         EVT_MENU(AppFrame::ID_LogWindow, AppFrame::OnID_LogWindow)
1020         EVT_MENU(AppFrame::ID_SaveFile, AppFrame::OnID_SaveFile)
1021         EVT_MENU(AppFrame::ID_OpenFile, AppFrame::OnID_OpenFile)
1022         EVT_MENU(wxID_EXIT, AppFrame::OnExitEvt)
1023         EVT_MENU(wxID_ABOUT, AppFrame::OnAbout)
1024
1025         EVT_TIMER(AppFrame::ID_Timer, AppFrame::OnID_Timer)
1026
1027         EVT_COMMAND(wxID_ANY, ConfigChangedEvent, AppFrame::onConfigChanged)
1028
1029         EVT_BUTTON(ID_BtnSave, AppFrame::doSave)
1030         EVT_BUTTON(ID_BtnLoad, AppFrame::doLoad)
1031
1032
1033 wxEND_EVENT_TABLE()
1034
1035
1036
1037 class App : public wxApp
1038 {
1039 public:
1040         virtual bool OnInit()
1041         {
1042                 AppFrame* frame = new AppFrame();
1043                 frame->Show(true);
1044                 SetTopWindow(frame);
1045                 return true;
1046         }
1047 };
1048 } // namespace
1049
1050 // Main Method
1051 wxIMPLEMENT_APP(App);
1052
1053