scsi2sd-util.cc 21 KB

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