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