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