scsi2sd-util.cc 23 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067
  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, 650)),
  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 + 1;
  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 (!progress->Update(
  738. (100 * currentProgress) / totalProgress,
  739. ss.str()
  740. )
  741. )
  742. {
  743. goto abort;
  744. }
  745. std::vector<uint8_t> flashData;
  746. try
  747. {
  748. myHID->readFlashRow(
  749. SCSI_CONFIG_ARRAY, flashRow + j, flashData);
  750. }
  751. catch (std::runtime_error& e)
  752. {
  753. mmLogStatus(e.what());
  754. goto err;
  755. }
  756. std::copy(
  757. flashData.begin(),
  758. flashData.end(),
  759. &raw[j * SCSI_CONFIG_ROW_SIZE]);
  760. }
  761. myTargets[i]->setConfig(ConfigUtil::fromBytes(&raw[0]));
  762. }
  763. myInitialConfig = true;
  764. mmLogStatus("Load Complete");
  765. while (progress->Update(100, "Load Complete"))
  766. {
  767. // Wait for the user to click "Close"
  768. wxMilliSleep(50);
  769. }
  770. goto out;
  771. err:
  772. mmLogStatus("Load failed");
  773. while (progress->Update(100, "Load failed"))
  774. {
  775. // Wait for the user to click "Close"
  776. wxMilliSleep(50);
  777. }
  778. goto out;
  779. abort:
  780. mmLogStatus("Load Aborted");
  781. out:
  782. return;
  783. }
  784. void doSave(wxCommandEvent& event)
  785. {
  786. TimerLock lock(myTimer);
  787. if (!myHID) return;
  788. mmLogStatus("Saving configuration");
  789. wxWindowPtr<wxGenericProgressDialog> progress(
  790. new wxGenericProgressDialog(
  791. "Save config settings",
  792. "Saving config settings",
  793. 100,
  794. this,
  795. wxPD_CAN_ABORT | wxPD_REMAINING_TIME)
  796. );
  797. int flashRow = SCSI_CONFIG_0_ROW;
  798. int currentProgress = 0;
  799. int totalProgress = myTargets.size() * SCSI_CONFIG_ROWS;
  800. for (size_t i = 0;
  801. i < myTargets.size();
  802. ++i, flashRow += SCSI_CONFIG_ROWS)
  803. {
  804. TargetConfig config(myTargets[i]->getConfig());
  805. std::vector<uint8_t> raw(ConfigUtil::toBytes(config));
  806. for (size_t j = 0; j < SCSI_CONFIG_ROWS; ++j)
  807. {
  808. std::stringstream ss;
  809. ss << "Programming flash array " << SCSI_CONFIG_ARRAY <<
  810. " row " << (flashRow + j);
  811. mmLogStatus(ss.str());
  812. currentProgress += 1;
  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. mmLogStatus("Save Complete");
  842. while (progress->Update(100, "Save Complete"))
  843. {
  844. // Wait for the user to click "Close"
  845. wxMilliSleep(50);
  846. }
  847. goto out;
  848. err:
  849. mmLogStatus("Save failed");
  850. while (progress->Update(100, "Save failed"))
  851. {
  852. // Wait for the user to click "Close"
  853. wxMilliSleep(50);
  854. }
  855. goto out;
  856. abort:
  857. mmLogStatus("Save Aborted");
  858. out:
  859. return;
  860. }
  861. // Note: Don't confuse this with the wxApp::OnExit virtual method
  862. void OnExitEvt(wxCommandEvent& event);
  863. void OnCloseEvt(wxCloseEvent& event);
  864. void OnAbout(wxCommandEvent& event)
  865. {
  866. wxMessageBox(
  867. "SCSI2SD (scsi2sd-util)\n"
  868. "Copyright (C) 2014 Michael McMaster <michael@codesrc.com>\n"
  869. "\n"
  870. "This program is free software: you can redistribute it and/or modify\n"
  871. "it under the terms of the GNU General Public License as published by\n"
  872. "the Free Software Foundation, either version 3 of the License, or\n"
  873. "(at your option) any later version.\n"
  874. "\n"
  875. "This program is distributed in the hope that it will be useful,\n"
  876. "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
  877. "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
  878. "GNU General Public License for more details.\n"
  879. "\n"
  880. "You should have received a copy of the GNU General Public License\n"
  881. "along with this program. If not, see <http://www.gnu.org/licenses/>.\n",
  882. "About scsi2sd-util", wxOK | wxICON_INFORMATION );
  883. }
  884. wxDECLARE_EVENT_TABLE();
  885. };
  886. wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
  887. EVT_MENU(AppFrame::ID_ConfigDefaults, AppFrame::OnID_ConfigDefaults)
  888. EVT_MENU(AppFrame::ID_Firmware, AppFrame::OnID_Firmware)
  889. EVT_MENU(AppFrame::ID_LogWindow, AppFrame::OnID_LogWindow)
  890. EVT_MENU(AppFrame::ID_SaveFile, AppFrame::OnID_SaveFile)
  891. EVT_MENU(AppFrame::ID_OpenFile, AppFrame::OnID_OpenFile)
  892. EVT_MENU(wxID_EXIT, AppFrame::OnExitEvt)
  893. EVT_MENU(wxID_ABOUT, AppFrame::OnAbout)
  894. EVT_TIMER(AppFrame::ID_Timer, AppFrame::OnID_Timer)
  895. EVT_COMMAND(wxID_ANY, ConfigChangedEvent, AppFrame::onConfigChanged)
  896. EVT_BUTTON(ID_BtnSave, AppFrame::doSave)
  897. EVT_BUTTON(ID_BtnLoad, AppFrame::doLoad)
  898. EVT_CLOSE(AppFrame::OnCloseEvt)
  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);
  914. void
  915. AppFrame::OnExitEvt(wxCommandEvent& event)
  916. {
  917. wxGetApp().ExitMainLoop();
  918. }
  919. void
  920. AppFrame::OnCloseEvt(wxCloseEvent& event)
  921. {
  922. wxGetApp().ExitMainLoop();
  923. }