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