scsi2sd-util.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832
  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. // For compilers that support precompilation, includes "wx/wx.h".
  18. #include <wx/wxprec.h>
  19. #ifndef WX_PRECOMP
  20. #include <wx/wx.h>
  21. #endif
  22. #include <wx/filedlg.h>
  23. #include <wx/filefn.h>
  24. #include <wx/filename.h>
  25. #include <wx/log.h>
  26. #include <wx/notebook.h>
  27. #include <wx/progdlg.h>
  28. #include <wx/utils.h>
  29. #include <wx/windowptr.h>
  30. #include <wx/thread.h>
  31. #include <zipper.hh>
  32. #include "ConfigUtil.hh"
  33. #include "TargetPanel.hh"
  34. #include "SCSI2SD_Bootloader.hh"
  35. #include "SCSI2SD_HID.hh"
  36. #include "Firmware.hh"
  37. #include <algorithm>
  38. #include <vector>
  39. #include <set>
  40. #include <sstream>
  41. #if __cplusplus >= 201103L
  42. #include <cstdint>
  43. #include <memory>
  44. using std::shared_ptr;
  45. #else
  46. #include <stdint.h>
  47. #include <tr1/memory>
  48. using std::tr1::shared_ptr;
  49. #endif
  50. #define MIN_FIRMWARE_VERSION 0x0400
  51. using namespace SCSI2SD;
  52. class ProgressWrapper
  53. {
  54. public:
  55. void setProgressDialog(
  56. const wxWindowPtr<wxGenericProgressDialog>& dlg,
  57. size_t maxRows)
  58. {
  59. myProgressDialog = dlg;
  60. myMaxRows = maxRows;
  61. myNumRows = 0;
  62. }
  63. void clearProgressDialog()
  64. {
  65. myProgressDialog->Show(false);
  66. myProgressDialog.reset();
  67. }
  68. void update(unsigned char arrayId, unsigned short rowNum)
  69. {
  70. if (!myProgressDialog) return;
  71. myNumRows++;
  72. std::stringstream ss;
  73. ss << "Writing flash array " <<
  74. static_cast<int>(arrayId) << " row " <<
  75. static_cast<int>(rowNum);
  76. wxLogMessage("%s", ss.str());
  77. myProgressDialog->Update(myNumRows, ss.str());
  78. }
  79. private:
  80. wxWindowPtr<wxGenericProgressDialog> myProgressDialog;
  81. size_t myMaxRows;
  82. size_t myNumRows;
  83. };
  84. static ProgressWrapper TheProgressWrapper;
  85. extern "C"
  86. void ProgressUpdate(unsigned char arrayId, unsigned short rowNum)
  87. {
  88. TheProgressWrapper.update(arrayId, rowNum);
  89. }
  90. namespace
  91. {
  92. class TimerLock
  93. {
  94. public:
  95. TimerLock(wxTimer* timer) :
  96. myTimer(timer),
  97. myInterval(myTimer->GetInterval())
  98. {
  99. myTimer->Stop();
  100. };
  101. virtual ~TimerLock()
  102. {
  103. if (myTimer && myInterval > 0)
  104. {
  105. myTimer->Start(myInterval);
  106. }
  107. }
  108. private:
  109. wxTimer* myTimer;
  110. int myInterval;
  111. };
  112. class AppFrame : public wxFrame
  113. {
  114. public:
  115. AppFrame() :
  116. wxFrame(NULL, wxID_ANY, "scsi2sd-util", wxPoint(50, 50), wxSize(600, 650)),
  117. myInitialConfig(false),
  118. myTickCounter(0)
  119. {
  120. wxMenu *menuFile = new wxMenu();
  121. menuFile->Append(
  122. ID_ConfigDefaults,
  123. "Load &Defaults",
  124. "Load default configuration options.");
  125. menuFile->Append(
  126. ID_Firmware,
  127. "&Upgrade Firmware...",
  128. "Upgrade or inspect device firmware version.");
  129. menuFile->AppendSeparator();
  130. menuFile->Append(wxID_EXIT);
  131. wxMenu *menuWindow= new wxMenu();
  132. menuWindow->Append(
  133. ID_LogWindow,
  134. "Show &Log",
  135. "Show debug log window");
  136. wxMenu *menuHelp = new wxMenu();
  137. menuHelp->Append(wxID_ABOUT);
  138. wxMenuBar *menuBar = new wxMenuBar();
  139. menuBar->Append( menuFile, "&File" );
  140. menuBar->Append( menuWindow, "&Window" );
  141. menuBar->Append( menuHelp, "&Help" );
  142. SetMenuBar( menuBar );
  143. CreateStatusBar();
  144. wxLogStatus(this, "Searching for SCSI2SD");
  145. {
  146. wxPanel* cfgPanel = new wxPanel(this);
  147. wxFlexGridSizer *fgs = new wxFlexGridSizer(3, 1, 15, 15);
  148. cfgPanel->SetSizer(fgs);
  149. // Empty space below menu bar.
  150. fgs->Add(5, 5, wxALL);
  151. wxNotebook* tabs = new wxNotebook(cfgPanel, ID_Notebook);
  152. for (int i = 0; i < MAX_SCSI_TARGETS; ++i)
  153. {
  154. TargetPanel* target =
  155. new TargetPanel(tabs, ConfigUtil::Default(i));
  156. myTargets.push_back(target);
  157. std::stringstream ss;
  158. ss << "Device " << (i + 1);
  159. tabs->AddPage(target, ss.str());
  160. target->Fit();
  161. }
  162. tabs->Fit();
  163. fgs->Add(tabs);
  164. wxPanel* btnPanel = new wxPanel(cfgPanel);
  165. wxFlexGridSizer *btnFgs = new wxFlexGridSizer(1, 2, 5, 5);
  166. btnPanel->SetSizer(btnFgs);
  167. myLoadButton =
  168. new wxButton(btnPanel, ID_BtnLoad, wxT("Load from device"));
  169. btnFgs->Add(myLoadButton);
  170. mySaveButton =
  171. new wxButton(btnPanel, ID_BtnSave, wxT("Save to device"));
  172. btnFgs->Add(mySaveButton);
  173. fgs->Add(btnPanel);
  174. btnPanel->Fit();
  175. cfgPanel->Fit();
  176. }
  177. //Fit(); // Needed to reduce window size on Windows
  178. FitInside(); // Needed on Linux to prevent status bar overlap
  179. myTimer = new wxTimer(this, ID_Timer);
  180. myTimer->Start(1000); //ms
  181. myLogWindow = new wxLogWindow(this, wxT("scsi2sd-util debug log"), true);
  182. }
  183. private:
  184. wxLogWindow* myLogWindow;
  185. std::vector<TargetPanel*> myTargets;
  186. wxButton* myLoadButton;
  187. wxButton* mySaveButton;
  188. wxTimer* myTimer;
  189. shared_ptr<HID> myHID;
  190. shared_ptr<Bootloader> myBootloader;
  191. bool myInitialConfig;
  192. uint8_t myTickCounter;
  193. void onConfigChanged(wxCommandEvent& event)
  194. {
  195. evaluate();
  196. }
  197. void evaluate()
  198. {
  199. bool valid = true;
  200. // Check for duplicate SCSI IDs
  201. std::set<uint8_t> enabledID;
  202. // Check for overlapping SD sectors.
  203. std::vector<std::pair<uint32_t, uint64_t> > sdSectors;
  204. bool isTargetEnabled = false; // Need at least one enabled
  205. uint32_t autoStartSector = 0;
  206. for (size_t i = 0; i < myTargets.size(); ++i)
  207. {
  208. myTargets[i]->setAutoStartSector(autoStartSector);
  209. valid = myTargets[i]->evaluate() && valid;
  210. if (myTargets[i]->isEnabled())
  211. {
  212. isTargetEnabled = true;
  213. uint8_t scsiID = myTargets[i]->getSCSIId();
  214. if (enabledID.find(scsiID) != enabledID.end())
  215. {
  216. myTargets[i]->setDuplicateID(true);
  217. valid = false;
  218. }
  219. else
  220. {
  221. enabledID.insert(scsiID);
  222. myTargets[i]->setDuplicateID(false);
  223. }
  224. auto sdSectorRange = myTargets[i]->getSDSectorRange();
  225. for (auto it(sdSectors.begin()); it != sdSectors.end(); ++it)
  226. {
  227. if (sdSectorRange.first < it->second &&
  228. sdSectorRange.second > it->first)
  229. {
  230. valid = false;
  231. myTargets[i]->setSDSectorOverlap(true);
  232. }
  233. else
  234. {
  235. myTargets[i]->setSDSectorOverlap(false);
  236. }
  237. }
  238. sdSectors.push_back(sdSectorRange);
  239. autoStartSector = sdSectorRange.second + 1;
  240. }
  241. else
  242. {
  243. myTargets[i]->setDuplicateID(false);
  244. myTargets[i]->setSDSectorOverlap(false);
  245. }
  246. }
  247. valid = valid && isTargetEnabled; // Need at least one.
  248. mySaveButton->Enable(
  249. valid &&
  250. myHID &&
  251. (myHID->getFirmwareVersion() >= MIN_FIRMWARE_VERSION));
  252. myLoadButton->Enable(
  253. myHID &&
  254. (myHID->getFirmwareVersion() >= MIN_FIRMWARE_VERSION));
  255. }
  256. enum
  257. {
  258. ID_ConfigDefaults = wxID_HIGHEST + 1,
  259. ID_Firmware,
  260. ID_Timer,
  261. ID_Notebook,
  262. ID_BtnLoad,
  263. ID_BtnSave,
  264. ID_LogWindow
  265. };
  266. void OnID_ConfigDefaults(wxCommandEvent& event)
  267. {
  268. for (size_t i = 0; i < myTargets.size(); ++i)
  269. {
  270. myTargets[i]->setConfig(ConfigUtil::Default(i));
  271. }
  272. }
  273. void OnID_Firmware(wxCommandEvent& event)
  274. {
  275. TimerLock lock(myTimer);
  276. doFirmwareUpdate();
  277. }
  278. void OnID_LogWindow(wxCommandEvent& event)
  279. {
  280. myLogWindow->Show();
  281. }
  282. void doFirmwareUpdate()
  283. {
  284. wxFileDialog dlg(
  285. this,
  286. "Load firmware file",
  287. "",
  288. "",
  289. "SCSI2SD Firmware files (*.scsi2sd;*.cyacd)|*.cyacd;*.scsi2sd",
  290. wxFD_OPEN | wxFD_FILE_MUST_EXIST);
  291. if (dlg.ShowModal() == wxID_CANCEL) return;
  292. std::string filename(dlg.GetPath());
  293. wxWindowPtr<wxGenericProgressDialog> progress(
  294. new wxGenericProgressDialog(
  295. "Searching for bootloader",
  296. "Searching for bootloader",
  297. 100,
  298. this,
  299. wxPD_AUTO_HIDE | wxPD_CAN_ABORT)
  300. );
  301. wxLogStatus(this, "Searching for bootloader");
  302. while (true)
  303. {
  304. try
  305. {
  306. if (!myHID) myHID.reset(HID::Open());
  307. if (myHID)
  308. {
  309. wxLogStatus(this, "Resetting SCSI2SD into bootloader");
  310. myHID->enterBootloader();
  311. myHID.reset();
  312. }
  313. if (!myBootloader)
  314. {
  315. myBootloader.reset(Bootloader::Open());
  316. if (myBootloader)
  317. {
  318. wxLogStatus(this, "Bootloader found");
  319. break;
  320. }
  321. }
  322. else if (myBootloader)
  323. {
  324. // Verify the USB HID connection is valid
  325. if (!myBootloader->ping())
  326. {
  327. wxLogStatus(this, "Bootloader ping failed");
  328. myBootloader.reset();
  329. }
  330. else
  331. {
  332. wxLogStatus(this, "Bootloader found");
  333. break;
  334. }
  335. }
  336. }
  337. catch (std::exception& e)
  338. {
  339. wxLogStatus(this, "%s", e.what());
  340. myHID.reset();
  341. myBootloader.reset();
  342. }
  343. wxMilliSleep(100);
  344. if (!progress->Pulse())
  345. {
  346. return; // user cancelled.
  347. }
  348. }
  349. int totalFlashRows = 0;
  350. std::string tmpFile;
  351. try
  352. {
  353. zipper::ReaderPtr reader(new zipper::FileReader(filename));
  354. zipper::Decompressor decomp(reader);
  355. std::vector<zipper::CompressedFilePtr> files(decomp.getEntries());
  356. for (auto it(files.begin()); it != files.end(); it++)
  357. {
  358. if (myBootloader->isCorrectFirmware((*it)->getPath()))
  359. {
  360. wxLogStatus(this,
  361. "Found firmware entry %s within archive %s",
  362. (*it)->getPath(),
  363. filename);
  364. tmpFile =
  365. wxFileName::CreateTempFileName(
  366. wxT("SCSI2SD_Firmware"), static_cast<wxFile*>(NULL)
  367. );
  368. zipper::FileWriter out(tmpFile);
  369. (*it)->decompress(out);
  370. wxLogStatus(this,
  371. "Firmware extracted to %s",
  372. tmpFile);
  373. break;
  374. }
  375. }
  376. if (tmpFile.empty())
  377. {
  378. // TODO allow "force" option
  379. wxMessageBox(
  380. "Wrong filename",
  381. "Wrong filename",
  382. wxOK | wxICON_ERROR);
  383. return;
  384. }
  385. Firmware firmware(tmpFile);
  386. totalFlashRows = firmware.totalFlashRows();
  387. }
  388. catch (std::exception& e)
  389. {
  390. wxLogStatus(this, "%s", e.what());
  391. std::stringstream msg;
  392. msg << "Could not open firmware file: " << e.what();
  393. wxMessageBox(
  394. msg.str(),
  395. "Bad file",
  396. wxOK | wxICON_ERROR);
  397. wxRemoveFile(tmpFile);
  398. return;
  399. }
  400. {
  401. wxWindowPtr<wxGenericProgressDialog> progress(
  402. new wxGenericProgressDialog(
  403. "Loading firmware",
  404. "Loading firmware",
  405. totalFlashRows,
  406. this,
  407. wxPD_AUTO_HIDE | wxPD_REMAINING_TIME)
  408. );
  409. TheProgressWrapper.setProgressDialog(progress, totalFlashRows);
  410. }
  411. wxLogStatus(this, "Upgrading firmware from file: %s", tmpFile);
  412. try
  413. {
  414. myBootloader->load(tmpFile, &ProgressUpdate);
  415. TheProgressWrapper.clearProgressDialog();
  416. wxMessageBox(
  417. "Firmware update successful",
  418. "Firmware OK",
  419. wxOK);
  420. wxLogStatus(this, "Firmware update successful");
  421. myHID.reset();
  422. myBootloader.reset();
  423. }
  424. catch (std::exception& e)
  425. {
  426. TheProgressWrapper.clearProgressDialog();
  427. wxLogStatus(this, "%s", e.what());
  428. myHID.reset();
  429. myBootloader.reset();
  430. wxMessageBox(
  431. "Firmware Update Failed",
  432. e.what(),
  433. wxOK | wxICON_ERROR);
  434. wxRemoveFile(tmpFile);
  435. }
  436. }
  437. void OnID_Timer(wxTimerEvent& event)
  438. {
  439. // Check if we are connected to the HID device.
  440. // AND/or bootloader device.
  441. try
  442. {
  443. if (myBootloader)
  444. {
  445. // Verify the USB HID connection is valid
  446. if (!myBootloader->ping())
  447. {
  448. myBootloader.reset();
  449. }
  450. }
  451. if (!myBootloader)
  452. {
  453. myBootloader.reset(Bootloader::Open());
  454. if (myBootloader)
  455. {
  456. wxLogStatus(this, "%s", "SCSI2SD Bootloader Ready");
  457. }
  458. }
  459. int supressLog = 0;
  460. if (myHID && myHID->getFirmwareVersion() < MIN_FIRMWARE_VERSION)
  461. {
  462. // No method to check connection is still valid.
  463. // So assume it isn't.
  464. myHID.reset();
  465. supressLog = 1;
  466. }
  467. else if (myHID && !myHID->ping())
  468. {
  469. // Verify the USB HID connection is valid
  470. myHID.reset();
  471. }
  472. if (!myHID)
  473. {
  474. myHID.reset(HID::Open());
  475. if (myHID)
  476. {
  477. if (myHID->getFirmwareVersion() < MIN_FIRMWARE_VERSION)
  478. {
  479. if (!supressLog)
  480. {
  481. // Oh dear, old firmware
  482. wxLogStatus(
  483. this,
  484. "Firmware update required. Version %s",
  485. myHID->getFirmwareVersionStr());
  486. }
  487. }
  488. else
  489. {
  490. wxLogStatus(
  491. this,
  492. "SCSI2SD Ready, firmware version %s",
  493. myHID->getFirmwareVersionStr());
  494. if (!myInitialConfig)
  495. {
  496. wxCommandEvent loadEvent(wxEVT_NULL, ID_BtnLoad);
  497. GetEventHandler()->AddPendingEvent(loadEvent);
  498. }
  499. }
  500. }
  501. else
  502. {
  503. char ticks[] = {'/', '-', '\\', '|'};
  504. std::stringstream ss;
  505. ss << "Searching for SCSI2SD device " << ticks[myTickCounter % sizeof(ticks)];
  506. myTickCounter++;
  507. SetStatusText(ss.str());
  508. }
  509. }
  510. }
  511. catch (std::runtime_error& e)
  512. {
  513. wxLogStatus(this, "%s", e.what());
  514. }
  515. evaluate();
  516. }
  517. void doLoad(wxCommandEvent& event)
  518. {
  519. TimerLock lock(myTimer);
  520. if (!myHID) return;
  521. wxLogStatus(this, "Loading configuration");
  522. wxWindowPtr<wxGenericProgressDialog> progress(
  523. new wxGenericProgressDialog(
  524. "Load config settings",
  525. "Loading config settings",
  526. 100,
  527. this,
  528. wxPD_CAN_ABORT | wxPD_REMAINING_TIME)
  529. );
  530. int flashRow = SCSI_CONFIG_0_ROW;
  531. int currentProgress = 0;
  532. int totalProgress = myTargets.size() * SCSI_CONFIG_ROWS;
  533. for (size_t i = 0;
  534. i < myTargets.size();
  535. ++i, flashRow += SCSI_CONFIG_ROWS)
  536. {
  537. std::vector<uint8_t> raw(sizeof(TargetConfig));
  538. for (size_t j = 0; j < SCSI_CONFIG_ROWS; ++j)
  539. {
  540. std::stringstream ss;
  541. ss << "Reading flash array " << SCSI_CONFIG_ARRAY <<
  542. " row " << (flashRow + j);
  543. wxLogStatus(this, "%s", ss.str());
  544. currentProgress += 1;
  545. if (!progress->Update(
  546. (100 * currentProgress) / totalProgress,
  547. ss.str()
  548. )
  549. )
  550. {
  551. goto abort;
  552. }
  553. std::vector<uint8_t> flashData;
  554. try
  555. {
  556. myHID->readFlashRow(
  557. SCSI_CONFIG_ARRAY, flashRow + j, flashData);
  558. }
  559. catch (std::runtime_error& e)
  560. {
  561. wxLogStatus(this, "%s", e.what());
  562. goto err;
  563. }
  564. std::copy(
  565. flashData.begin(),
  566. flashData.end(),
  567. &raw[j * SCSI_CONFIG_ROW_SIZE]);
  568. }
  569. myTargets[i]->setConfig(ConfigUtil::fromBytes(&raw[0]));
  570. }
  571. myInitialConfig = true;
  572. wxLogStatus(this, "%s", "Load Complete");
  573. while (progress->Update(100, "Load Complete"))
  574. {
  575. // Wait for the user to click "Close"
  576. wxMilliSleep(50);
  577. }
  578. goto out;
  579. err:
  580. wxLogStatus(this, "%s", "Load failed");
  581. while (progress->Update(100, "Load failed"))
  582. {
  583. // Wait for the user to click "Close"
  584. wxMilliSleep(50);
  585. }
  586. goto out;
  587. abort:
  588. wxLogStatus(this, "Load Aborted");
  589. out:
  590. return;
  591. }
  592. void doSave(wxCommandEvent& event)
  593. {
  594. TimerLock lock(myTimer);
  595. if (!myHID) return;
  596. wxLogStatus(this, "Saving configuration");
  597. wxWindowPtr<wxGenericProgressDialog> progress(
  598. new wxGenericProgressDialog(
  599. "Save config settings",
  600. "Saving config settings",
  601. 100,
  602. this,
  603. wxPD_CAN_ABORT | wxPD_REMAINING_TIME)
  604. );
  605. int flashRow = SCSI_CONFIG_0_ROW;
  606. int currentProgress = 0;
  607. int totalProgress = myTargets.size() * SCSI_CONFIG_ROWS;
  608. for (size_t i = 0;
  609. i < myTargets.size();
  610. ++i, flashRow += SCSI_CONFIG_ROWS)
  611. {
  612. TargetConfig config(myTargets[i]->getConfig());
  613. std::vector<uint8_t> raw(ConfigUtil::toBytes(config));
  614. for (size_t j = 0; j < SCSI_CONFIG_ROWS; ++j)
  615. {
  616. std::stringstream ss;
  617. ss << "Programming flash array " << SCSI_CONFIG_ARRAY <<
  618. " row " << (flashRow + j);
  619. wxLogStatus(this, "%s", ss.str());
  620. currentProgress += 1;
  621. if (!progress->Update(
  622. (100 * currentProgress) / totalProgress,
  623. ss.str()
  624. )
  625. )
  626. {
  627. goto abort;
  628. }
  629. std::vector<uint8_t> flashData(SCSI_CONFIG_ROW_SIZE, 0);
  630. std::copy(
  631. &raw[j * SCSI_CONFIG_ROW_SIZE],
  632. &raw[(1+j) * SCSI_CONFIG_ROW_SIZE],
  633. flashData.begin());
  634. try
  635. {
  636. myHID->writeFlashRow(
  637. SCSI_CONFIG_ARRAY, flashRow + j, flashData);
  638. }
  639. catch (std::runtime_error& e)
  640. {
  641. wxLogStatus(this, "%s", e.what());
  642. goto err;
  643. }
  644. }
  645. }
  646. // Reboot so new settings take effect.
  647. myHID->enterBootloader();
  648. myHID.reset();
  649. wxLogStatus(this, "Save Complete");
  650. while (progress->Update(100, "Save Complete"))
  651. {
  652. // Wait for the user to click "Close"
  653. wxMilliSleep(50);
  654. }
  655. goto out;
  656. err:
  657. wxLogStatus(this, "Save failed");
  658. while (progress->Update(100, "Save failed"))
  659. {
  660. // Wait for the user to click "Close"
  661. wxMilliSleep(50);
  662. }
  663. goto out;
  664. abort:
  665. wxLogStatus(this, "Save Aborted");
  666. out:
  667. (void) true; // empty statement.
  668. }
  669. void OnExit(wxCommandEvent& event)
  670. {
  671. Close(true);
  672. }
  673. void OnAbout(wxCommandEvent& event)
  674. {
  675. wxMessageBox(
  676. "SCSI2SD (scsi2sd-util)\n"
  677. "Copyright (C) 2014 Michael McMaster <michael@codesrc.com>\n"
  678. "\n"
  679. "This program is free software: you can redistribute it and/or modify\n"
  680. "it under the terms of the GNU General Public License as published by\n"
  681. "the Free Software Foundation, either version 3 of the License, or\n"
  682. "(at your option) any later version.\n"
  683. "\n"
  684. "This program is distributed in the hope that it will be useful,\n"
  685. "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
  686. "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
  687. "GNU General Public License for more details.\n"
  688. "\n"
  689. "You should have received a copy of the GNU General Public License\n"
  690. "along with this program. If not, see <http://www.gnu.org/licenses/>.\n",
  691. "About scsi2sd-util", wxOK | wxICON_INFORMATION );
  692. }
  693. wxDECLARE_EVENT_TABLE();
  694. };
  695. wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
  696. EVT_MENU(AppFrame::ID_ConfigDefaults, AppFrame::OnID_ConfigDefaults)
  697. EVT_MENU(AppFrame::ID_Firmware, AppFrame::OnID_Firmware)
  698. EVT_MENU(AppFrame::ID_LogWindow, AppFrame::OnID_LogWindow)
  699. EVT_MENU(wxID_EXIT, AppFrame::OnExit)
  700. EVT_MENU(wxID_ABOUT, AppFrame::OnAbout)
  701. EVT_TIMER(AppFrame::ID_Timer, AppFrame::OnID_Timer)
  702. EVT_COMMAND(wxID_ANY, ConfigChangedEvent, AppFrame::onConfigChanged)
  703. EVT_BUTTON(ID_BtnSave, AppFrame::doSave)
  704. EVT_BUTTON(ID_BtnLoad, AppFrame::doLoad)
  705. wxEND_EVENT_TABLE()
  706. class App : public wxApp
  707. {
  708. public:
  709. virtual bool OnInit()
  710. {
  711. AppFrame* frame = new AppFrame();
  712. frame->Show(true);
  713. SetTopWindow(frame);
  714. return true;
  715. }
  716. };
  717. } // namespace
  718. // Main Method
  719. wxIMPLEMENT_APP(App);