scsi2sd-util.cc 23 KB

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