scsi2sd-util.cc 25 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147
  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. myBoardPanel->setConfig(ConfigUtil::DefaultBoardConfig());
  330. for (size_t i = 0; i < myTargets.size(); ++i)
  331. {
  332. myTargets[i]->setConfig(ConfigUtil::Default(i));
  333. }
  334. }
  335. void OnID_SaveFile(wxCommandEvent& event)
  336. {
  337. TimerLock lock(myTimer);
  338. wxFileDialog dlg(
  339. this,
  340. "Save config settings",
  341. "",
  342. "",
  343. "XML files (*.xml)|*.xml",
  344. wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
  345. if (dlg.ShowModal() == wxID_CANCEL) return;
  346. wxFileOutputStream file(dlg.GetPath());
  347. if (!file.IsOk())
  348. {
  349. wxLogError("Cannot save settings to file '%s'.", dlg.GetPath());
  350. return;
  351. }
  352. wxTextOutputStream s(file);
  353. s << "<SCSI2SD>\n";
  354. s << ConfigUtil::toXML(myBoardPanel->getConfig());
  355. for (size_t i = 0; i < myTargets.size(); ++i)
  356. {
  357. s << ConfigUtil::toXML(myTargets[i]->getConfig());
  358. }
  359. s << "</SCSI2SD>\n";
  360. }
  361. void OnID_OpenFile(wxCommandEvent& event)
  362. {
  363. TimerLock lock(myTimer);
  364. wxFileDialog dlg(
  365. this,
  366. "Load config settings",
  367. "",
  368. "",
  369. "XML files (*.xml)|*.xml",
  370. wxFD_OPEN | wxFD_FILE_MUST_EXIST);
  371. if (dlg.ShowModal() == wxID_CANCEL) return;
  372. try
  373. {
  374. std::pair<BoardConfig, std::vector<TargetConfig>> configs(
  375. ConfigUtil::fromXML(std::string(dlg.GetPath())));
  376. myBoardPanel->setConfig(configs.first);
  377. size_t i;
  378. for (i = 0; i < configs.second.size() && i < myTargets.size(); ++i)
  379. {
  380. myTargets[i]->setConfig(configs.second[i]);
  381. }
  382. for (; i < myTargets.size(); ++i)
  383. {
  384. myTargets[i]->setConfig(ConfigUtil::Default(i));
  385. }
  386. }
  387. catch (std::exception& e)
  388. {
  389. wxLogError(
  390. "Cannot load settings from file '%s'.\n%s",
  391. dlg.GetPath(),
  392. e.what());
  393. wxMessageBox(
  394. e.what(),
  395. "Load error",
  396. wxOK | wxICON_ERROR);
  397. }
  398. }
  399. void OnID_Firmware(wxCommandEvent& event)
  400. {
  401. TimerLock lock(myTimer);
  402. doFirmwareUpdate();
  403. }
  404. void OnID_LogWindow(wxCommandEvent& event)
  405. {
  406. myLogWindow->Show();
  407. }
  408. void doFirmwareUpdate()
  409. {
  410. wxFileDialog dlg(
  411. this,
  412. "Load firmware file",
  413. "",
  414. "",
  415. "SCSI2SD Firmware files (*.scsi2sd;*.cyacd)|*.cyacd;*.scsi2sd",
  416. wxFD_OPEN | wxFD_FILE_MUST_EXIST);
  417. if (dlg.ShowModal() == wxID_CANCEL) return;
  418. std::string filename(dlg.GetPath());
  419. wxWindowPtr<wxGenericProgressDialog> progress(
  420. new wxGenericProgressDialog(
  421. "Searching for bootloader",
  422. "Searching for bootloader",
  423. 100,
  424. this,
  425. wxPD_AUTO_HIDE | wxPD_CAN_ABORT)
  426. );
  427. mmLogStatus("Searching for bootloader");
  428. while (true)
  429. {
  430. try
  431. {
  432. if (!myHID) myHID.reset(HID::Open());
  433. if (myHID)
  434. {
  435. mmLogStatus("Resetting SCSI2SD into bootloader");
  436. myHID->enterBootloader();
  437. myHID.reset();
  438. }
  439. if (!myBootloader)
  440. {
  441. myBootloader.reset(Bootloader::Open());
  442. if (myBootloader)
  443. {
  444. mmLogStatus("Bootloader found");
  445. break;
  446. }
  447. }
  448. else if (myBootloader)
  449. {
  450. // Verify the USB HID connection is valid
  451. if (!myBootloader->ping())
  452. {
  453. mmLogStatus("Bootloader ping failed");
  454. myBootloader.reset();
  455. }
  456. else
  457. {
  458. mmLogStatus("Bootloader found");
  459. break;
  460. }
  461. }
  462. }
  463. catch (std::exception& e)
  464. {
  465. mmLogStatus(e.what());
  466. myHID.reset();
  467. myBootloader.reset();
  468. }
  469. wxMilliSleep(100);
  470. if (!progress->Pulse())
  471. {
  472. return; // user cancelled.
  473. }
  474. }
  475. int totalFlashRows = 0;
  476. std::string tmpFile;
  477. try
  478. {
  479. zipper::ReaderPtr reader(new zipper::FileReader(filename));
  480. zipper::Decompressor decomp(reader);
  481. std::vector<zipper::CompressedFilePtr> files(decomp.getEntries());
  482. for (auto it(files.begin()); it != files.end(); it++)
  483. {
  484. if (myBootloader->isCorrectFirmware((*it)->getPath()))
  485. {
  486. std::stringstream msg;
  487. msg << "Found firmware entry " << (*it)->getPath() <<
  488. " within archive " << filename;
  489. mmLogStatus(msg.str());
  490. tmpFile =
  491. wxFileName::CreateTempFileName(
  492. _("SCSI2SD_Firmware"), static_cast<wxFile*>(NULL)
  493. );
  494. zipper::FileWriter out(tmpFile);
  495. (*it)->decompress(out);
  496. msg.clear();
  497. msg << "Firmware extracted to " << tmpFile;
  498. mmLogStatus(msg.str());
  499. break;
  500. }
  501. }
  502. if (tmpFile.empty())
  503. {
  504. // TODO allow "force" option
  505. wxMessageBox(
  506. "Wrong filename",
  507. "Wrong filename",
  508. wxOK | wxICON_ERROR);
  509. return;
  510. }
  511. Firmware firmware(tmpFile);
  512. totalFlashRows = firmware.totalFlashRows();
  513. }
  514. catch (std::exception& e)
  515. {
  516. mmLogStatus(e.what());
  517. std::stringstream msg;
  518. msg << "Could not open firmware file: " << e.what();
  519. wxMessageBox(
  520. msg.str(),
  521. "Bad file",
  522. wxOK | wxICON_ERROR);
  523. wxRemoveFile(tmpFile);
  524. return;
  525. }
  526. {
  527. wxWindowPtr<wxGenericProgressDialog> progress(
  528. new wxGenericProgressDialog(
  529. "Loading firmware",
  530. "Loading firmware",
  531. totalFlashRows,
  532. this,
  533. wxPD_AUTO_HIDE | wxPD_REMAINING_TIME)
  534. );
  535. TheProgressWrapper.setProgressDialog(progress, totalFlashRows);
  536. }
  537. std::stringstream msg;
  538. msg << "Upgrading firmware from file: " << tmpFile;
  539. mmLogStatus(msg.str());
  540. try
  541. {
  542. myBootloader->load(tmpFile, &ProgressUpdate);
  543. TheProgressWrapper.clearProgressDialog();
  544. wxMessageBox(
  545. "Firmware update successful",
  546. "Firmware OK",
  547. wxOK);
  548. mmLogStatus("Firmware update successful");
  549. myHID.reset();
  550. myBootloader.reset();
  551. }
  552. catch (std::exception& e)
  553. {
  554. TheProgressWrapper.clearProgressDialog();
  555. mmLogStatus(e.what());
  556. myHID.reset();
  557. myBootloader.reset();
  558. wxMessageBox(
  559. "Firmware Update Failed",
  560. e.what(),
  561. wxOK | wxICON_ERROR);
  562. wxRemoveFile(tmpFile);
  563. }
  564. }
  565. void dumpSCSICommand(std::vector<uint8_t> buf)
  566. {
  567. std::stringstream msg;
  568. msg << std::hex;
  569. for (size_t i = 0; i < 32 && i < buf.size(); ++i)
  570. {
  571. msg << std::setfill('0') << std::setw(2) <<
  572. static_cast<int>(buf[i]) << ' ';
  573. }
  574. wxLogMessage(this, msg.str().c_str());
  575. }
  576. void logSCSI()
  577. {
  578. if (!mySCSILogChk->IsChecked() ||
  579. !myHID)
  580. {
  581. return;
  582. }
  583. try
  584. {
  585. std::vector<uint8_t> info(HID::HID_PACKET_SIZE);
  586. if (myHID->readSCSIDebugInfo(info))
  587. {
  588. dumpSCSICommand(info);
  589. }
  590. }
  591. catch (std::exception& e)
  592. {
  593. wxLogWarning(this, e.what());
  594. myHID.reset();
  595. }
  596. }
  597. void OnID_Timer(wxTimerEvent& event)
  598. {
  599. logSCSI();
  600. time_t now = time(NULL);
  601. if (now == myLastPollTime) return;
  602. myLastPollTime = now;
  603. // Check if we are connected to the HID device.
  604. // AND/or bootloader device.
  605. try
  606. {
  607. if (myBootloader)
  608. {
  609. // Verify the USB HID connection is valid
  610. if (!myBootloader->ping())
  611. {
  612. myBootloader.reset();
  613. }
  614. }
  615. if (!myBootloader)
  616. {
  617. myBootloader.reset(Bootloader::Open());
  618. if (myBootloader)
  619. {
  620. mmLogStatus("SCSI2SD Bootloader Ready");
  621. }
  622. }
  623. int supressLog = 0;
  624. if (myHID && myHID->getFirmwareVersion() < MIN_FIRMWARE_VERSION)
  625. {
  626. // No method to check connection is still valid.
  627. // So assume it isn't.
  628. myHID.reset();
  629. supressLog = 1;
  630. }
  631. else if (myHID && !myHID->ping())
  632. {
  633. // Verify the USB HID connection is valid
  634. myHID.reset();
  635. }
  636. if (!myHID)
  637. {
  638. myHID.reset(HID::Open());
  639. if (myHID)
  640. {
  641. if (myHID->getFirmwareVersion() < MIN_FIRMWARE_VERSION)
  642. {
  643. if (!supressLog)
  644. {
  645. // Oh dear, old firmware
  646. std::stringstream msg;
  647. msg << "Firmware update required. Version " <<
  648. myHID->getFirmwareVersionStr();
  649. mmLogStatus(msg.str());
  650. }
  651. }
  652. else
  653. {
  654. std::stringstream msg;
  655. msg << "SCSI2SD Ready, firmware version " <<
  656. myHID->getFirmwareVersionStr();
  657. mmLogStatus(msg.str());
  658. std::vector<uint8_t> csd(myHID->getSD_CSD());
  659. std::vector<uint8_t> cid(myHID->getSD_CID());
  660. std::stringstream sdinfo;
  661. sdinfo << "SD Capacity (512-byte sectors): " <<
  662. myHID->getSDCapacity() << std::endl;
  663. sdinfo << "SD CSD Register: ";
  664. if (sdCrc7(&csd[0], 15, 0) != (csd[15] >> 1))
  665. {
  666. sdinfo << "BADCRC ";
  667. }
  668. for (size_t i = 0; i < csd.size(); ++i)
  669. {
  670. sdinfo <<
  671. std::hex << std::setfill('0') << std::setw(2) <<
  672. static_cast<int>(csd[i]);
  673. }
  674. sdinfo << std::endl;
  675. sdinfo << "SD CID Register: ";
  676. if (sdCrc7(&cid[0], 15, 0) != (cid[15] >> 1))
  677. {
  678. sdinfo << "BADCRC ";
  679. }
  680. for (size_t i = 0; i < cid.size(); ++i)
  681. {
  682. sdinfo <<
  683. std::hex << std::setfill('0') << std::setw(2) <<
  684. static_cast<int>(cid[i]);
  685. }
  686. wxLogMessage(this, "%s", sdinfo.str());
  687. if (mySelfTestChk->IsChecked())
  688. {
  689. std::stringstream scsiInfo;
  690. scsiInfo << "SCSI Self-Test: " <<
  691. (myHID->scsiSelfTest() ? "Passed" : "FAIL");
  692. wxLogMessage(this, "%s", scsiInfo.str());
  693. }
  694. if (!myInitialConfig)
  695. {
  696. /* This doesn't work properly, and causes crashes.
  697. wxCommandEvent loadEvent(wxEVT_NULL, ID_BtnLoad);
  698. GetEventHandler()->AddPendingEvent(loadEvent);
  699. */
  700. }
  701. }
  702. }
  703. else
  704. {
  705. char ticks[] = {'/', '-', '\\', '|'};
  706. std::stringstream ss;
  707. ss << "Searching for SCSI2SD device " << ticks[myTickCounter % sizeof(ticks)];
  708. myTickCounter++;
  709. SetStatusText(ss.str());
  710. }
  711. }
  712. }
  713. catch (std::runtime_error& e)
  714. {
  715. std::cerr << e.what() << std::endl;
  716. mmLogStatus(e.what());
  717. }
  718. evaluate();
  719. }
  720. void doLoad(wxCommandEvent& event)
  721. {
  722. TimerLock lock(myTimer);
  723. if (!myHID) return;
  724. mmLogStatus("Loading configuration");
  725. wxWindowPtr<wxGenericProgressDialog> progress(
  726. new wxGenericProgressDialog(
  727. "Load config settings",
  728. "Loading config settings",
  729. 100,
  730. this,
  731. wxPD_CAN_ABORT | wxPD_REMAINING_TIME)
  732. );
  733. int currentProgress = 0;
  734. int totalProgress = myTargets.size() * SCSI_CONFIG_ROWS + 1;
  735. // Read board config first.
  736. std::vector<uint8_t> boardCfgFlashData;
  737. int flashRow = SCSI_CONFIG_BOARD_ROW;
  738. {
  739. std::stringstream ss;
  740. ss << "Reading flash array " << SCSI_CONFIG_ARRAY <<
  741. " row " << flashRow;
  742. mmLogStatus(ss.str());
  743. currentProgress += 1;
  744. if (!progress->Update(
  745. (100 * currentProgress) / totalProgress,
  746. ss.str()
  747. )
  748. )
  749. {
  750. goto abort;
  751. }
  752. try
  753. {
  754. myHID->readFlashRow(
  755. SCSI_CONFIG_ARRAY, flashRow, boardCfgFlashData);
  756. myBoardPanel->setConfig(
  757. ConfigUtil::boardConfigFromBytes(&boardCfgFlashData[0]));
  758. }
  759. catch (std::runtime_error& e)
  760. {
  761. mmLogStatus(e.what());
  762. goto err;
  763. }
  764. }
  765. flashRow = SCSI_CONFIG_0_ROW;
  766. for (size_t i = 0;
  767. i < myTargets.size();
  768. ++i, flashRow += SCSI_CONFIG_ROWS)
  769. {
  770. std::vector<uint8_t> raw(sizeof(TargetConfig));
  771. for (size_t j = 0; j < SCSI_CONFIG_ROWS; ++j)
  772. {
  773. std::stringstream ss;
  774. ss << "Reading flash array " << SCSI_CONFIG_ARRAY <<
  775. " row " << (flashRow + j);
  776. mmLogStatus(ss.str());
  777. currentProgress += 1;
  778. if (currentProgress == totalProgress)
  779. {
  780. ss.str("Load Complete.");
  781. mmLogStatus("Load Complete.");
  782. }
  783. if (!progress->Update(
  784. (100 * currentProgress) / totalProgress,
  785. ss.str()
  786. )
  787. )
  788. {
  789. goto abort;
  790. }
  791. std::vector<uint8_t> flashData;
  792. try
  793. {
  794. myHID->readFlashRow(
  795. SCSI_CONFIG_ARRAY, flashRow + j, flashData);
  796. }
  797. catch (std::runtime_error& e)
  798. {
  799. mmLogStatus(e.what());
  800. goto err;
  801. }
  802. std::copy(
  803. flashData.begin(),
  804. flashData.end(),
  805. &raw[j * SCSI_CONFIG_ROW_SIZE]);
  806. }
  807. myTargets[i]->setConfig(ConfigUtil::fromBytes(&raw[0]));
  808. }
  809. // Support old boards without board config set
  810. if (memcmp(&boardCfgFlashData[0], "BCFG", 4)) {
  811. BoardConfig defCfg = ConfigUtil::DefaultBoardConfig();
  812. defCfg.flags = myTargets[0]->getConfig().flagsDEPRECATED;
  813. myBoardPanel->setConfig(defCfg);
  814. }
  815. myInitialConfig = true;
  816. goto out;
  817. err:
  818. mmLogStatus("Load failed");
  819. progress->Update(100, "Load failed");
  820. goto out;
  821. abort:
  822. mmLogStatus("Load Aborted");
  823. out:
  824. return;
  825. }
  826. void doSave(wxCommandEvent& event)
  827. {
  828. TimerLock lock(myTimer);
  829. if (!myHID) return;
  830. mmLogStatus("Saving configuration");
  831. wxWindowPtr<wxGenericProgressDialog> progress(
  832. new wxGenericProgressDialog(
  833. "Save config settings",
  834. "Saving config settings",
  835. 100,
  836. this,
  837. wxPD_CAN_ABORT | wxPD_REMAINING_TIME)
  838. );
  839. int currentProgress = 0;
  840. int totalProgress = myTargets.size() * SCSI_CONFIG_ROWS + 1;
  841. // Write board config first.
  842. int flashRow = SCSI_CONFIG_BOARD_ROW;
  843. {
  844. std::stringstream ss;
  845. ss << "Programming flash array " << SCSI_CONFIG_ARRAY <<
  846. " row " << flashRow;
  847. mmLogStatus(ss.str());
  848. currentProgress += 1;
  849. if (!progress->Update(
  850. (100 * currentProgress) / totalProgress,
  851. ss.str()
  852. )
  853. )
  854. {
  855. goto abort;
  856. }
  857. std::vector<uint8_t> flashData =
  858. ConfigUtil::boardConfigToBytes(myBoardPanel->getConfig());
  859. try
  860. {
  861. myHID->writeFlashRow(
  862. SCSI_CONFIG_ARRAY, flashRow, flashData);
  863. }
  864. catch (std::runtime_error& e)
  865. {
  866. mmLogStatus(e.what());
  867. goto err;
  868. }
  869. }
  870. flashRow = SCSI_CONFIG_0_ROW;
  871. for (size_t i = 0;
  872. i < myTargets.size();
  873. ++i, flashRow += SCSI_CONFIG_ROWS)
  874. {
  875. TargetConfig config(myTargets[i]->getConfig());
  876. std::vector<uint8_t> raw(ConfigUtil::toBytes(config));
  877. for (size_t j = 0; j < SCSI_CONFIG_ROWS; ++j)
  878. {
  879. std::stringstream ss;
  880. ss << "Programming flash array " << SCSI_CONFIG_ARRAY <<
  881. " row " << (flashRow + j);
  882. mmLogStatus(ss.str());
  883. currentProgress += 1;
  884. if (currentProgress == totalProgress)
  885. {
  886. ss.str("Save Complete.");
  887. mmLogStatus("Save Complete.");
  888. }
  889. if (!progress->Update(
  890. (100 * currentProgress) / totalProgress,
  891. ss.str()
  892. )
  893. )
  894. {
  895. goto abort;
  896. }
  897. std::vector<uint8_t> flashData(SCSI_CONFIG_ROW_SIZE, 0);
  898. std::copy(
  899. &raw[j * SCSI_CONFIG_ROW_SIZE],
  900. &raw[(1+j) * SCSI_CONFIG_ROW_SIZE],
  901. flashData.begin());
  902. try
  903. {
  904. myHID->writeFlashRow(
  905. SCSI_CONFIG_ARRAY, flashRow + j, flashData);
  906. }
  907. catch (std::runtime_error& e)
  908. {
  909. mmLogStatus(e.what());
  910. goto err;
  911. }
  912. }
  913. }
  914. // Reboot so new settings take effect.
  915. myHID->enterBootloader();
  916. myHID.reset();
  917. goto out;
  918. err:
  919. mmLogStatus("Save failed");
  920. progress->Update(100, "Save failed");
  921. goto out;
  922. abort:
  923. mmLogStatus("Save Aborted");
  924. out:
  925. return;
  926. }
  927. // Note: Don't confuse this with the wxApp::OnExit virtual method
  928. void OnExitEvt(wxCommandEvent& event);
  929. void OnCloseEvt(wxCloseEvent& event);
  930. void OnAbout(wxCommandEvent& event)
  931. {
  932. wxMessageBox(
  933. "SCSI2SD (scsi2sd-util)\n"
  934. "Copyright (C) 2014 Michael McMaster <michael@codesrc.com>\n"
  935. "\n"
  936. "This program is free software: you can redistribute it and/or modify\n"
  937. "it under the terms of the GNU General Public License as published by\n"
  938. "the Free Software Foundation, either version 3 of the License, or\n"
  939. "(at your option) any later version.\n"
  940. "\n"
  941. "This program is distributed in the hope that it will be useful,\n"
  942. "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
  943. "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
  944. "GNU General Public License for more details.\n"
  945. "\n"
  946. "You should have received a copy of the GNU General Public License\n"
  947. "along with this program. If not, see <http://www.gnu.org/licenses/>.\n",
  948. "About scsi2sd-util", wxOK | wxICON_INFORMATION );
  949. }
  950. wxDECLARE_EVENT_TABLE();
  951. };
  952. wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
  953. EVT_MENU(AppFrame::ID_ConfigDefaults, AppFrame::OnID_ConfigDefaults)
  954. EVT_MENU(AppFrame::ID_Firmware, AppFrame::OnID_Firmware)
  955. EVT_MENU(AppFrame::ID_LogWindow, AppFrame::OnID_LogWindow)
  956. EVT_MENU(AppFrame::ID_SaveFile, AppFrame::OnID_SaveFile)
  957. EVT_MENU(AppFrame::ID_OpenFile, AppFrame::OnID_OpenFile)
  958. EVT_MENU(wxID_EXIT, AppFrame::OnExitEvt)
  959. EVT_MENU(wxID_ABOUT, AppFrame::OnAbout)
  960. EVT_TIMER(AppFrame::ID_Timer, AppFrame::OnID_Timer)
  961. EVT_COMMAND(wxID_ANY, ConfigChangedEvent, AppFrame::onConfigChanged)
  962. EVT_BUTTON(ID_BtnSave, AppFrame::doSave)
  963. EVT_BUTTON(ID_BtnLoad, AppFrame::doLoad)
  964. EVT_CLOSE(AppFrame::OnCloseEvt)
  965. wxEND_EVENT_TABLE()
  966. class App : public wxApp
  967. {
  968. public:
  969. virtual bool OnInit()
  970. {
  971. AppFrame* frame = new AppFrame();
  972. frame->Show(true);
  973. SetTopWindow(frame);
  974. return true;
  975. }
  976. };
  977. } // namespace
  978. // Main Method
  979. wxIMPLEMENT_APP(App);
  980. void
  981. AppFrame::OnExitEvt(wxCommandEvent& event)
  982. {
  983. wxGetApp().ExitMainLoop();
  984. }
  985. void
  986. AppFrame::OnCloseEvt(wxCloseEvent& event)
  987. {
  988. wxGetApp().ExitMainLoop();
  989. }