scsi2sd-util.cc 22 KB

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