Small compatibility improvements, and added scsi2sd-monitor test program
[SCSI2SD-V6.git] / software / scsi2sd-util / scsi2sd-util.cc
CommitLineData
75f825e6
MM
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>
638c94ce
MM
26#include <wx/filefn.h>
27#include <wx/filename.h>
28#include <wx/log.h>
75f825e6
MM
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
b19a0a4c
MM
35#include <zipper.hh>
36
75f825e6
MM
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>
5446dc86 44#include <iomanip>
75f825e6
MM
45#include <vector>
46#include <set>
47#include <sstream>
48
49#if __cplusplus >= 201103L
50#include <cstdint>
51#include <memory>
52using std::shared_ptr;
53#else
54#include <stdint.h>
55#include <tr1/memory>
56using std::tr1::shared_ptr;
57#endif
58
638c94ce 59#define MIN_FIRMWARE_VERSION 0x0400
75f825e6
MM
60
61using namespace SCSI2SD;
62
63class ProgressWrapper
64{
65public:
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 {
638c94ce 77 myProgressDialog->Show(false);
75f825e6
MM
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);
638c94ce 91 wxLogMessage("%s", ss.str());
75f825e6
MM
92 myProgressDialog->Update(myNumRows, ss.str());
93 }
94
95private:
96 wxWindowPtr<wxGenericProgressDialog> myProgressDialog;
97 size_t myMaxRows;
98 size_t myNumRows;
99};
100static ProgressWrapper TheProgressWrapper;
101
102extern "C"
103void ProgressUpdate(unsigned char arrayId, unsigned short rowNum)
104{
105 TheProgressWrapper.update(arrayId, rowNum);
106}
107
108namespace
109{
110
9ad7cc15
MM
111static 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
75f825e6
MM
131class TimerLock
132{
133public:
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 }
148private:
149 wxTimer* myTimer;
150 int myInterval;
151};
152
153class AppFrame : public wxFrame
154{
155public:
156 AppFrame() :
157 wxFrame(NULL, wxID_ANY, "scsi2sd-util", wxPoint(50, 50), wxSize(600, 650)),
158 myInitialConfig(false),
5446dc86
MM
159 myTickCounter(0),
160 myLastPollTime(0)
75f825e6
MM
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);
638c94ce
MM
173
174 wxMenu *menuWindow= new wxMenu();
175 menuWindow->Append(
176 ID_LogWindow,
177 "Show &Log",
178 "Show debug log window");
179
5446dc86
MM
180 wxMenu *menuDebug = new wxMenu();
181 mySCSILogChk = menuDebug->AppendCheckItem(
182 ID_SCSILog,
183 "Log SCSI data",
184 "Log SCSI commands");
185
9ad7cc15
MM
186 mySelfTestChk = menuDebug->AppendCheckItem(
187 ID_SelfTest,
188 "SCSI Standalone Self-Test",
189 "SCSI Standalone Self-Test");
190
75f825e6
MM
191 wxMenu *menuHelp = new wxMenu();
192 menuHelp->Append(wxID_ABOUT);
638c94ce 193
75f825e6
MM
194 wxMenuBar *menuBar = new wxMenuBar();
195 menuBar->Append( menuFile, "&File" );
5446dc86 196 menuBar->Append( menuDebug, "&Debug" );
638c94ce 197 menuBar->Append( menuWindow, "&Window" );
75f825e6
MM
198 menuBar->Append( menuHelp, "&Help" );
199 SetMenuBar( menuBar );
200
201 CreateStatusBar();
75f825e6
MM
202
203 {
204 wxPanel* cfgPanel = new wxPanel(this);
638c94ce
MM
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
75f825e6
MM
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());
638c94ce 221 target->Fit();
75f825e6 222 }
638c94ce 223 tabs->Fit();
75f825e6
MM
224 fgs->Add(tabs);
225
226
227 wxPanel* btnPanel = new wxPanel(cfgPanel);
228 wxFlexGridSizer *btnFgs = new wxFlexGridSizer(1, 2, 5, 5);
638c94ce
MM
229 btnPanel->SetSizer(btnFgs);
230 myLoadButton =
231 new wxButton(btnPanel, ID_BtnLoad, wxT("Load from device"));
232 btnFgs->Add(myLoadButton);
75f825e6
MM
233 mySaveButton =
234 new wxButton(btnPanel, ID_BtnSave, wxT("Save to device"));
235 btnFgs->Add(mySaveButton);
638c94ce 236 fgs->Add(btnPanel);
75f825e6 237
638c94ce
MM
238 btnPanel->Fit();
239 cfgPanel->Fit();
75f825e6 240 }
638c94ce
MM
241 //Fit(); // Needed to reduce window size on Windows
242 FitInside(); // Needed on Linux to prevent status bar overlap
75f825e6 243
638c94ce 244 myLogWindow = new wxLogWindow(this, wxT("scsi2sd-util debug log"), true);
5446dc86
MM
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
75f825e6 249 }
5446dc86 250
75f825e6 251private:
638c94ce 252 wxLogWindow* myLogWindow;
75f825e6 253 std::vector<TargetPanel*> myTargets;
638c94ce 254 wxButton* myLoadButton;
75f825e6 255 wxButton* mySaveButton;
5446dc86 256 wxMenuItem* mySCSILogChk;
9ad7cc15 257 wxMenuItem* mySelfTestChk;
75f825e6
MM
258 wxTimer* myTimer;
259 shared_ptr<HID> myHID;
260 shared_ptr<Bootloader> myBootloader;
261 bool myInitialConfig;
262
263 uint8_t myTickCounter;
264
5446dc86
MM
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
75f825e6
MM
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
638c94ce 291 uint32_t autoStartSector = 0;
75f825e6
MM
292 for (size_t i = 0; i < myTargets.size(); ++i)
293 {
638c94ce 294 myTargets[i]->setAutoStartSector(autoStartSector);
75f825e6
MM
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);
638c94ce 327 autoStartSector = sdSectorRange.second + 1;
75f825e6
MM
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
638c94ce
MM
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));
75f825e6
MM
346 }
347
348
349 enum
350 {
351 ID_ConfigDefaults = wxID_HIGHEST + 1,
352 ID_Firmware,
353 ID_Timer,
354 ID_Notebook,
355 ID_BtnLoad,
638c94ce 356 ID_BtnSave,
5446dc86 357 ID_LogWindow,
9ad7cc15
MM
358 ID_SCSILog,
359 ID_SelfTest
75f825e6
MM
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
638c94ce
MM
376 void OnID_LogWindow(wxCommandEvent& event)
377 {
378 myLogWindow->Show();
379 }
380
75f825e6
MM
381 void doFirmwareUpdate()
382 {
383 wxFileDialog dlg(
384 this,
385 "Load firmware file",
386 "",
387 "",
638c94ce 388 "SCSI2SD Firmware files (*.scsi2sd;*.cyacd)|*.cyacd;*.scsi2sd",
75f825e6
MM
389 wxFD_OPEN | wxFD_FILE_MUST_EXIST);
390 if (dlg.ShowModal() == wxID_CANCEL) return;
391
392 std::string filename(dlg.GetPath());
393
638c94ce
MM
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 );
5446dc86 402 mmLogStatus("Searching for bootloader");
638c94ce 403 while (true)
75f825e6 404 {
638c94ce 405 try
75f825e6 406 {
638c94ce
MM
407 if (!myHID) myHID.reset(HID::Open());
408 if (myHID)
409 {
5446dc86 410 mmLogStatus("Resetting SCSI2SD into bootloader");
75f825e6 411
638c94ce
MM
412 myHID->enterBootloader();
413 myHID.reset();
414 }
75f825e6 415
638c94ce
MM
416
417 if (!myBootloader)
75f825e6 418 {
638c94ce
MM
419 myBootloader.reset(Bootloader::Open());
420 if (myBootloader)
421 {
5446dc86 422 mmLogStatus("Bootloader found");
638c94ce
MM
423 break;
424 }
75f825e6 425 }
75f825e6 426
638c94ce
MM
427 else if (myBootloader)
428 {
429 // Verify the USB HID connection is valid
430 if (!myBootloader->ping())
431 {
5446dc86 432 mmLogStatus("Bootloader ping failed");
638c94ce
MM
433 myBootloader.reset();
434 }
435 else
436 {
5446dc86 437 mmLogStatus("Bootloader found");
638c94ce
MM
438 break;
439 }
440 }
441 }
442 catch (std::exception& e)
75f825e6 443 {
5446dc86 444 mmLogStatus(e.what());
638c94ce
MM
445 myHID.reset();
446 myBootloader.reset();
447 }
448 wxMilliSleep(100);
449 if (!progress->Pulse())
450 {
451 return; // user cancelled.
75f825e6 452 }
75f825e6
MM
453 }
454
455 int totalFlashRows = 0;
638c94ce 456 std::string tmpFile;
75f825e6
MM
457 try
458 {
638c94ce
MM
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 {
5446dc86
MM
466 std::stringstream msg;
467 msg << "Found firmware entry " << (*it)->getPath() <<
468 " within archive " << filename;
469 mmLogStatus(msg.str());
638c94ce
MM
470 tmpFile =
471 wxFileName::CreateTempFileName(
472 wxT("SCSI2SD_Firmware"), static_cast<wxFile*>(NULL)
473 );
474 zipper::FileWriter out(tmpFile);
475 (*it)->decompress(out);
5446dc86
MM
476 msg.clear();
477 msg << "Firmware extracted to " << tmpFile;
478 mmLogStatus(msg.str());
638c94ce
MM
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);
75f825e6
MM
494 totalFlashRows = firmware.totalFlashRows();
495 }
496 catch (std::exception& e)
497 {
5446dc86 498 mmLogStatus(e.what());
75f825e6
MM
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);
638c94ce 505 wxRemoveFile(tmpFile);
75f825e6
MM
506 return;
507 }
508
509 {
510 wxWindowPtr<wxGenericProgressDialog> progress(
511 new wxGenericProgressDialog(
512 "Loading firmware",
513 "Loading firmware",
514 totalFlashRows,
515 this,
638c94ce 516 wxPD_AUTO_HIDE | wxPD_REMAINING_TIME)
75f825e6
MM
517 );
518 TheProgressWrapper.setProgressDialog(progress, totalFlashRows);
519 }
520
5446dc86
MM
521 std::stringstream msg;
522 msg << "Upgrading firmware from file: " << tmpFile;
523 mmLogStatus(msg.str());
75f825e6
MM
524
525 try
526 {
638c94ce 527 myBootloader->load(tmpFile, &ProgressUpdate);
75f825e6
MM
528 TheProgressWrapper.clearProgressDialog();
529
530 wxMessageBox(
531 "Firmware update successful",
532 "Firmware OK",
533 wxOK);
5446dc86 534 mmLogStatus("Firmware update successful");
75f825e6
MM
535
536
537 myHID.reset();
538 myBootloader.reset();
539 }
540 catch (std::exception& e)
541 {
542 TheProgressWrapper.clearProgressDialog();
5446dc86 543 mmLogStatus(e.what());
75f825e6
MM
544 myHID.reset();
545 myBootloader.reset();
546
547 wxMessageBox(
548 "Firmware Update Failed",
549 e.what(),
550 wxOK | wxICON_ERROR);
638c94ce
MM
551
552 wxRemoveFile(tmpFile);
75f825e6
MM
553 }
554 }
555
5446dc86
MM
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
75f825e6
MM
585 void OnID_Timer(wxTimerEvent& event)
586 {
5446dc86
MM
587 logSCSI();
588 time_t now = time(NULL);
589 if (now == myLastPollTime) return;
590 myLastPollTime = now;
591
75f825e6
MM
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 {
5446dc86 611 mmLogStatus("SCSI2SD Bootloader Ready");
75f825e6
MM
612 }
613 }
614
638c94ce
MM
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())
75f825e6
MM
624 {
625 // Verify the USB HID connection is valid
638c94ce 626 myHID.reset();
75f825e6
MM
627 }
628
629 if (!myHID)
630 {
631 myHID.reset(HID::Open());
632 if (myHID)
633 {
638c94ce 634 if (myHID->getFirmwareVersion() < MIN_FIRMWARE_VERSION)
75f825e6 635 {
638c94ce
MM
636 if (!supressLog)
637 {
638 // Oh dear, old firmware
5446dc86
MM
639 std::stringstream msg;
640 msg << "Firmware update required. Version " <<
641 myHID->getFirmwareVersionStr();
642 mmLogStatus(msg.str());
638c94ce 643 }
75f825e6
MM
644 }
645 else
646 {
5446dc86
MM
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: ";
9ad7cc15
MM
659 if (sdCrc7(&csd[0], 15, 0) != (csd[15] >> 1))
660 {
661 sdinfo << "BADCRC ";
662 }
5446dc86
MM
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: ";
9ad7cc15
MM
671 if (sdCrc7(&cid[0], 15, 0) != (cid[15] >> 1))
672 {
673 sdinfo << "BADCRC ";
674 }
5446dc86
MM
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());
75f825e6 683
9ad7cc15
MM
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
75f825e6
MM
692 if (!myInitialConfig)
693 {
694 wxCommandEvent loadEvent(wxEVT_NULL, ID_BtnLoad);
695 GetEventHandler()->AddPendingEvent(loadEvent);
696 }
5446dc86 697
75f825e6
MM
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 {
5446dc86
MM
712 std::cerr << e.what() << std::endl;
713 mmLogStatus(e.what());
75f825e6
MM
714 }
715
716 evaluate();
717 }
718
719 void doLoad(wxCommandEvent& event)
720 {
721 TimerLock lock(myTimer);
722 if (!myHID) return;
723
5446dc86 724 mmLogStatus("Loading configuration");
75f825e6
MM
725
726 wxWindowPtr<wxGenericProgressDialog> progress(
727 new wxGenericProgressDialog(
728 "Load config settings",
729 "Loading config settings",
730 100,
731 this,
638c94ce 732 wxPD_CAN_ABORT | wxPD_REMAINING_TIME)
75f825e6
MM
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);
5446dc86 749 mmLogStatus(ss.str());
75f825e6
MM
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 {
5446dc86 770 mmLogStatus(e.what());
75f825e6
MM
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;
5446dc86 783 mmLogStatus("Load Complete");
75f825e6
MM
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:
5446dc86 792 mmLogStatus("Load failed");
75f825e6
MM
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:
5446dc86 801 mmLogStatus("Load Aborted");
75f825e6
MM
802
803 out:
804 return;
805 }
806
807 void doSave(wxCommandEvent& event)
808 {
809 TimerLock lock(myTimer);
810 if (!myHID) return;
811
5446dc86 812 mmLogStatus("Saving configuration");
75f825e6
MM
813
814 wxWindowPtr<wxGenericProgressDialog> progress(
815 new wxGenericProgressDialog(
816 "Save config settings",
817 "Saving config settings",
818 100,
819 this,
638c94ce 820 wxPD_CAN_ABORT | wxPD_REMAINING_TIME)
75f825e6
MM
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);
5446dc86 838 mmLogStatus(ss.str());
75f825e6
MM
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 {
5446dc86 861 mmLogStatus(e.what());
75f825e6
MM
862 goto err;
863 }
864 }
865 }
866
867 // Reboot so new settings take effect.
868 myHID->enterBootloader();
869 myHID.reset();
870
5446dc86 871 mmLogStatus("Save Complete");
75f825e6
MM
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:
5446dc86 880 mmLogStatus("Save failed");
75f825e6
MM
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:
5446dc86 889 mmLogStatus("Save Aborted");
75f825e6
MM
890
891 out:
5446dc86 892 return;
75f825e6
MM
893 }
894
95b51978
MM
895 // Note: Don't confuse this with the wxApp::OnExit virtual method
896 void OnExitEvt(wxCommandEvent& event)
75f825e6
MM
897 {
898 Close(true);
899 }
95b51978 900
75f825e6
MM
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
926wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
927 EVT_MENU(AppFrame::ID_ConfigDefaults, AppFrame::OnID_ConfigDefaults)
928 EVT_MENU(AppFrame::ID_Firmware, AppFrame::OnID_Firmware)
638c94ce 929 EVT_MENU(AppFrame::ID_LogWindow, AppFrame::OnID_LogWindow)
95b51978 930 EVT_MENU(wxID_EXIT, AppFrame::OnExitEvt)
75f825e6
MM
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
941wxEND_EVENT_TABLE()
942
943
944
945class App : public wxApp
946{
947public:
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
959wxIMPLEMENT_APP(App);
960
961