scsi2sd-util.cc 25 KB

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