scsi2sd-util.cc 23 KB

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