#endif
#include <wx/filedlg.h>
+#include <wx/filefn.h>
+#include <wx/filename.h>
+#include <wx/log.h>
#include <wx/notebook.h>
#include <wx/progdlg.h>
#include <wx/utils.h>
#include "Firmware.hh"
#include <algorithm>
+#include <iomanip>
#include <vector>
#include <set>
#include <sstream>
using std::tr1::shared_ptr;
#endif
+#define MIN_FIRMWARE_VERSION 0x0400
using namespace SCSI2SD;
void clearProgressDialog()
{
+ myProgressDialog->Show(false);
myProgressDialog.reset();
}
ss << "Writing flash array " <<
static_cast<int>(arrayId) << " row " <<
static_cast<int>(rowNum);
- std::clog << ss.str() << std::endl;
+ wxLogMessage("%s", ss.str());
myProgressDialog->Update(myNumRows, ss.str());
}
AppFrame() :
wxFrame(NULL, wxID_ANY, "scsi2sd-util", wxPoint(50, 50), wxSize(600, 650)),
myInitialConfig(false),
- myTickCounter(0)
+ myTickCounter(0),
+ myLastPollTime(0)
{
wxMenu *menuFile = new wxMenu();
menuFile->Append(
"Upgrade or inspect device firmware version.");
menuFile->AppendSeparator();
menuFile->Append(wxID_EXIT);
+
+ wxMenu *menuWindow= new wxMenu();
+ menuWindow->Append(
+ ID_LogWindow,
+ "Show &Log",
+ "Show debug log window");
+
+ wxMenu *menuDebug = new wxMenu();
+ mySCSILogChk = menuDebug->AppendCheckItem(
+ ID_SCSILog,
+ "Log SCSI data",
+ "Log SCSI commands");
+
wxMenu *menuHelp = new wxMenu();
menuHelp->Append(wxID_ABOUT);
+
wxMenuBar *menuBar = new wxMenuBar();
menuBar->Append( menuFile, "&File" );
+ menuBar->Append( menuDebug, "&Debug" );
+ menuBar->Append( menuWindow, "&Window" );
menuBar->Append( menuHelp, "&Help" );
SetMenuBar( menuBar );
CreateStatusBar();
- SetStatusText( "Searching for SCSI2SD" );
{
wxPanel* cfgPanel = new wxPanel(this);
- wxFlexGridSizer *fgs = new wxFlexGridSizer(2, 1, 5, 5);
+ wxFlexGridSizer *fgs = new wxFlexGridSizer(3, 1, 15, 15);
+ cfgPanel->SetSizer(fgs);
+
+ // Empty space below menu bar.
+ fgs->Add(5, 5, wxALL);
+
wxNotebook* tabs = new wxNotebook(cfgPanel, ID_Notebook);
for (int i = 0; i < MAX_SCSI_TARGETS; ++i)
std::stringstream ss;
ss << "Device " << (i + 1);
tabs->AddPage(target, ss.str());
+ target->Fit();
}
+ tabs->Fit();
fgs->Add(tabs);
wxPanel* btnPanel = new wxPanel(cfgPanel);
wxFlexGridSizer *btnFgs = new wxFlexGridSizer(1, 2, 5, 5);
- btnFgs->Add(new wxButton(btnPanel, ID_BtnLoad, wxT("Load from device")));
+ btnPanel->SetSizer(btnFgs);
+ myLoadButton =
+ new wxButton(btnPanel, ID_BtnLoad, wxT("Load from device"));
+ btnFgs->Add(myLoadButton);
mySaveButton =
new wxButton(btnPanel, ID_BtnSave, wxT("Save to device"));
btnFgs->Add(mySaveButton);
- {
- wxBoxSizer* hbox = new wxBoxSizer(wxHORIZONTAL);
- hbox->Add(btnFgs, 1, wxALL | wxEXPAND, 15);
- btnPanel->SetSizer(hbox);
- fgs->Add(btnPanel);
- }
+ fgs->Add(btnPanel);
- {
- wxBoxSizer* hbox = new wxBoxSizer(wxHORIZONTAL);
- hbox->Add(fgs, 1, wxALL | wxEXPAND, 15);
- cfgPanel->SetSizer(hbox);
- }
+ btnPanel->Fit();
+ cfgPanel->Fit();
}
+ //Fit(); // Needed to reduce window size on Windows
+ FitInside(); // Needed on Linux to prevent status bar overlap
- myTimer = new wxTimer(this, ID_Timer);
- myTimer->Start(1000); //ms
+ myLogWindow = new wxLogWindow(this, wxT("scsi2sd-util debug log"), true);
+ myLogWindow->PassMessages(false); // Prevent messagebox popups
+ myTimer = new wxTimer(this, ID_Timer);
+ myTimer->Start(16); //ms, suitable for scsi debug logging
}
+
private:
+ wxLogWindow* myLogWindow;
std::vector<TargetPanel*> myTargets;
+ wxButton* myLoadButton;
wxButton* mySaveButton;
+ wxMenuItem* mySCSILogChk;
wxTimer* myTimer;
shared_ptr<HID> myHID;
shared_ptr<Bootloader> myBootloader;
uint8_t myTickCounter;
+ time_t myLastPollTime;
+
+ void mmLogStatus(const std::string& msg)
+ {
+ // We set PassMessages to false on our log window to prevent popups, but
+ // this also prevents wxLogStatus from updating the status bar.
+ SetStatusText(msg);
+ wxLogMessage(this, "%s", msg.c_str());
+ }
+
void onConfigChanged(wxCommandEvent& event)
{
evaluate();
std::vector<std::pair<uint32_t, uint64_t> > sdSectors;
bool isTargetEnabled = false; // Need at least one enabled
-
+ uint32_t autoStartSector = 0;
for (size_t i = 0; i < myTargets.size(); ++i)
{
+ myTargets[i]->setAutoStartSector(autoStartSector);
valid = myTargets[i]->evaluate() && valid;
if (myTargets[i]->isEnabled())
}
}
sdSectors.push_back(sdSectorRange);
+ autoStartSector = sdSectorRange.second + 1;
}
else
{
valid = valid && isTargetEnabled; // Need at least one.
- mySaveButton->Enable(valid && myHID);
+ mySaveButton->Enable(
+ valid &&
+ myHID &&
+ (myHID->getFirmwareVersion() >= MIN_FIRMWARE_VERSION));
+
+ myLoadButton->Enable(
+ myHID &&
+ (myHID->getFirmwareVersion() >= MIN_FIRMWARE_VERSION));
}
ID_Timer,
ID_Notebook,
ID_BtnLoad,
- ID_BtnSave
+ ID_BtnSave,
+ ID_LogWindow,
+ ID_SCSILog
};
void OnID_ConfigDefaults(wxCommandEvent& event)
doFirmwareUpdate();
}
+ void OnID_LogWindow(wxCommandEvent& event)
+ {
+ myLogWindow->Show();
+ }
+
void doFirmwareUpdate()
{
wxFileDialog dlg(
"Load firmware file",
"",
"",
- "SCSI2SD Firmware files (*.cyacd)|*.cyacd",
+ "SCSI2SD Firmware files (*.scsi2sd;*.cyacd)|*.cyacd;*.scsi2sd",
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dlg.ShowModal() == wxID_CANCEL) return;
std::string filename(dlg.GetPath());
- try
+ wxWindowPtr<wxGenericProgressDialog> progress(
+ new wxGenericProgressDialog(
+ "Searching for bootloader",
+ "Searching for bootloader",
+ 100,
+ this,
+ wxPD_AUTO_HIDE | wxPD_CAN_ABORT)
+ );
+ mmLogStatus("Searching for bootloader");
+ while (true)
{
- if (!myHID) myHID.reset(HID::Open());
- if (myHID)
+ try
{
- std::clog << "Resetting SCSI2SD into bootloader" << std::endl;
+ if (!myHID) myHID.reset(HID::Open());
+ if (myHID)
+ {
+ mmLogStatus("Resetting SCSI2SD into bootloader");
- myHID->enterBootloader();
- myHID.reset();
- }
+ myHID->enterBootloader();
+ myHID.reset();
+ }
- if (myBootloader)
- {
- // Verify the USB HID connection is valid
- if (!myBootloader->ping())
+
+ if (!myBootloader)
{
- myBootloader.reset();
+ myBootloader.reset(Bootloader::Open());
+ if (myBootloader)
+ {
+ mmLogStatus("Bootloader found");
+ break;
+ }
}
- }
- for (int i = 0; !myBootloader && (i < 10); ++i)
+ else if (myBootloader)
+ {
+ // Verify the USB HID connection is valid
+ if (!myBootloader->ping())
+ {
+ mmLogStatus("Bootloader ping failed");
+ myBootloader.reset();
+ }
+ else
+ {
+ mmLogStatus("Bootloader found");
+ break;
+ }
+ }
+ }
+ catch (std::exception& e)
{
- std::clog << "Searching for bootloader" << std::endl;
- myBootloader.reset(Bootloader::Open());
- wxMilliSleep(100);
+ mmLogStatus(e.what());
+ myHID.reset();
+ myBootloader.reset();
+ }
+ wxMilliSleep(100);
+ if (!progress->Pulse())
+ {
+ return; // user cancelled.
}
- }
- catch (std::exception& e)
- {
- std::clog << e.what() << std::endl;
-
- SetStatusText(e.what());
- myHID.reset();
- myBootloader.reset();
- }
-
- if (!myBootloader)
- {
- wxMessageBox(
- "SCSI2SD not found",
- "Device not ready",
- wxOK | wxICON_ERROR);
-
- return;
- }
-
- if (!myBootloader->isCorrectFirmware(filename))
- {
- // TODO allow "force" option
- wxMessageBox(
- "Wrong filename",
- "Wrong filename",
- wxOK | wxICON_ERROR);
- return;
}
int totalFlashRows = 0;
+ std::string tmpFile;
try
{
- Firmware firmware(filename);
+ zipper::ReaderPtr reader(new zipper::FileReader(filename));
+ zipper::Decompressor decomp(reader);
+ std::vector<zipper::CompressedFilePtr> files(decomp.getEntries());
+ for (auto it(files.begin()); it != files.end(); it++)
+ {
+ if (myBootloader->isCorrectFirmware((*it)->getPath()))
+ {
+ std::stringstream msg;
+ msg << "Found firmware entry " << (*it)->getPath() <<
+ " within archive " << filename;
+ mmLogStatus(msg.str());
+ tmpFile =
+ wxFileName::CreateTempFileName(
+ wxT("SCSI2SD_Firmware"), static_cast<wxFile*>(NULL)
+ );
+ zipper::FileWriter out(tmpFile);
+ (*it)->decompress(out);
+ msg.clear();
+ msg << "Firmware extracted to " << tmpFile;
+ mmLogStatus(msg.str());
+ break;
+ }
+ }
+
+ if (tmpFile.empty())
+ {
+ // TODO allow "force" option
+ wxMessageBox(
+ "Wrong filename",
+ "Wrong filename",
+ wxOK | wxICON_ERROR);
+ return;
+ }
+
+ Firmware firmware(tmpFile);
totalFlashRows = firmware.totalFlashRows();
}
catch (std::exception& e)
{
- SetStatusText(e.what());
+ mmLogStatus(e.what());
std::stringstream msg;
msg << "Could not open firmware file: " << e.what();
wxMessageBox(
msg.str(),
"Bad file",
wxOK | wxICON_ERROR);
+ wxRemoveFile(tmpFile);
return;
}
"Loading firmware",
totalFlashRows,
this,
- wxPD_APP_MODAL)
+ wxPD_AUTO_HIDE | wxPD_REMAINING_TIME)
);
TheProgressWrapper.setProgressDialog(progress, totalFlashRows);
}
- std::clog << "Upgrading firmware from file: " << filename << std::endl;
+ std::stringstream msg;
+ msg << "Upgrading firmware from file: " << tmpFile;
+ mmLogStatus(msg.str());
try
{
- myBootloader->load(filename, &ProgressUpdate);
+ myBootloader->load(tmpFile, &ProgressUpdate);
TheProgressWrapper.clearProgressDialog();
wxMessageBox(
"Firmware update successful",
"Firmware OK",
wxOK);
- SetStatusText("Firmware update successful");
+ mmLogStatus("Firmware update successful");
myHID.reset();
catch (std::exception& e)
{
TheProgressWrapper.clearProgressDialog();
- SetStatusText(e.what());
+ mmLogStatus(e.what());
myHID.reset();
myBootloader.reset();
"Firmware Update Failed",
e.what(),
wxOK | wxICON_ERROR);
+
+ wxRemoveFile(tmpFile);
+ }
+ }
+
+ void logSCSI()
+ {
+ if (!mySCSILogChk->IsChecked() ||
+ !myHID)
+ {
+ return;
+ }
+ try
+ {
+ std::vector<uint8_t> info(HID::HID_PACKET_SIZE);
+ if (myHID->readSCSIDebugInfo(info))
+ {
+ std::stringstream msg;
+ msg << std::hex;
+ for (size_t i = 0; i < 32 && i < info.size(); ++i)
+ {
+ msg << std::setfill('0') << std::setw(2) <<
+ static_cast<int>(info[i]) << ' ';
+ }
+ wxLogMessage(this, msg.str().c_str());
+ }
+ }
+ catch (std::exception& e)
+ {
+ wxLogWarning(this, e.what());
+ myHID.reset();
}
}
void OnID_Timer(wxTimerEvent& event)
{
+ logSCSI();
+ time_t now = time(NULL);
+ if (now == myLastPollTime) return;
+ myLastPollTime = now;
+
// Check if we are connected to the HID device.
// AND/or bootloader device.
try
if (myBootloader)
{
- SetStatusText(wxT("SCSI2SD Bootloader Ready"));
+ mmLogStatus("SCSI2SD Bootloader Ready");
}
}
- if (myHID)
+ int supressLog = 0;
+ if (myHID && myHID->getFirmwareVersion() < MIN_FIRMWARE_VERSION)
+ {
+ // No method to check connection is still valid.
+ // So assume it isn't.
+ myHID.reset();
+ supressLog = 1;
+ }
+ else if (myHID && !myHID->ping())
{
// Verify the USB HID connection is valid
- if (!myHID->ping())
- {
- myHID.reset();
- }
+ myHID.reset();
}
if (!myHID)
myHID.reset(HID::Open());
if (myHID)
{
- uint16_t version = myHID->getFirmwareVersion();
- if (version == 0)
+ if (myHID->getFirmwareVersion() < MIN_FIRMWARE_VERSION)
{
- // Oh dear, old firmware
- SetStatusText(wxT("Firmware update required"));
- myHID.reset();
+ if (!supressLog)
+ {
+ // Oh dear, old firmware
+ std::stringstream msg;
+ msg << "Firmware update required. Version " <<
+ myHID->getFirmwareVersionStr();
+ mmLogStatus(msg.str());
+ }
}
else
{
- SetStatusText(wxT("SCSI2SD Ready"));
+ std::stringstream msg;
+ msg << "SCSI2SD Ready, firmware version " <<
+ myHID->getFirmwareVersionStr();
+ mmLogStatus(msg.str());
+
+ std::vector<uint8_t> csd(myHID->getSD_CSD());
+ std::vector<uint8_t> cid(myHID->getSD_CID());
+ std::stringstream sdinfo;
+ sdinfo << "SD Capacity (512-byte sectors): " <<
+ myHID->getSDCapacity() << std::endl;
+
+ sdinfo << "SD CSD Register: ";
+ for (size_t i = 0; i < csd.size(); ++i)
+ {
+ sdinfo <<
+ std::hex << std::setfill('0') << std::setw(2) <<
+ static_cast<int>(csd[i]);
+ }
+ sdinfo << std::endl;
+ sdinfo << "SD CID Register: ";
+ for (size_t i = 0; i < cid.size(); ++i)
+ {
+ sdinfo <<
+ std::hex << std::setfill('0') << std::setw(2) <<
+ static_cast<int>(cid[i]);
+ }
+
+ wxLogMessage(this, "%s", sdinfo.str());
if (!myInitialConfig)
{
wxCommandEvent loadEvent(wxEVT_NULL, ID_BtnLoad);
GetEventHandler()->AddPendingEvent(loadEvent);
}
+
}
}
else
}
catch (std::runtime_error& e)
{
- std::clog << e.what() << std::endl;
- SetStatusText(e.what());
+ std::cerr << e.what() << std::endl;
+ mmLogStatus(e.what());
}
evaluate();
TimerLock lock(myTimer);
if (!myHID) return;
- std::clog << "Loading configuration" << std::endl;
+ mmLogStatus("Loading configuration");
wxWindowPtr<wxGenericProgressDialog> progress(
new wxGenericProgressDialog(
"Loading config settings",
100,
this,
- wxPD_APP_MODAL | wxPD_CAN_ABORT)
+ wxPD_CAN_ABORT | wxPD_REMAINING_TIME)
);
int flashRow = SCSI_CONFIG_0_ROW;
std::stringstream ss;
ss << "Reading flash array " << SCSI_CONFIG_ARRAY <<
" row " << (flashRow + j);
- SetStatusText(ss.str());
- std::clog << ss.str() << std::endl;
+ mmLogStatus(ss.str());
currentProgress += 1;
if (!progress->Update(
(100 * currentProgress) / totalProgress,
}
catch (std::runtime_error& e)
{
- SetStatusText(e.what());
+ mmLogStatus(e.what());
goto err;
}
}
myInitialConfig = true;
- SetStatusText("Load Complete");
- std::clog << "Load Complete" << std::endl;
+ mmLogStatus("Load Complete");
while (progress->Update(100, "Load Complete"))
{
// Wait for the user to click "Close"
goto out;
err:
- SetStatusText("Load failed");
- std::clog << "Load failed" << std::endl;
+ mmLogStatus("Load failed");
while (progress->Update(100, "Load failed"))
{
// Wait for the user to click "Close"
goto out;
abort:
- SetStatusText("Load Aborted");
- std::clog << "Load Aborted" << std::endl;
+ mmLogStatus("Load Aborted");
out:
return;
TimerLock lock(myTimer);
if (!myHID) return;
- std::clog << "Saving configuration" << std::endl;
+ mmLogStatus("Saving configuration");
wxWindowPtr<wxGenericProgressDialog> progress(
new wxGenericProgressDialog(
"Saving config settings",
100,
this,
- wxPD_APP_MODAL | wxPD_CAN_ABORT)
+ wxPD_CAN_ABORT | wxPD_REMAINING_TIME)
);
int flashRow = SCSI_CONFIG_0_ROW;
std::stringstream ss;
ss << "Programming flash array " << SCSI_CONFIG_ARRAY <<
" row " << (flashRow + j);
- SetStatusText(ss.str());
- std::clog << ss.str() << std::endl;
+ mmLogStatus(ss.str());
currentProgress += 1;
if (!progress->Update(
(100 * currentProgress) / totalProgress,
}
catch (std::runtime_error& e)
{
- std::clog << e.what() << std::endl;
- SetStatusText(e.what());
+ mmLogStatus(e.what());
goto err;
}
}
myHID->enterBootloader();
myHID.reset();
- SetStatusText("Save Complete");
- std::clog << "Save Complete" << std::endl;
+ mmLogStatus("Save Complete");
while (progress->Update(100, "Save Complete"))
{
// Wait for the user to click "Close"
goto out;
err:
- SetStatusText("Save failed");
- std::clog << "Save failed" << std::endl;
+ mmLogStatus("Save failed");
while (progress->Update(100, "Save failed"))
{
// Wait for the user to click "Close"
goto out;
abort:
- SetStatusText("Save Aborted");
- std::clog << "Save Aborted" << std::endl;
+ mmLogStatus("Save Aborted");
out:
- (void) true; // empty statement.
+ return;
}
- void OnExit(wxCommandEvent& event)
+ // Note: Don't confuse this with the wxApp::OnExit virtual method
+ void OnExitEvt(wxCommandEvent& event)
{
Close(true);
}
+
void OnAbout(wxCommandEvent& event)
{
wxMessageBox(
wxBEGIN_EVENT_TABLE(AppFrame, wxFrame)
EVT_MENU(AppFrame::ID_ConfigDefaults, AppFrame::OnID_ConfigDefaults)
EVT_MENU(AppFrame::ID_Firmware, AppFrame::OnID_Firmware)
- EVT_MENU(wxID_EXIT, AppFrame::OnExit)
+ EVT_MENU(AppFrame::ID_LogWindow, AppFrame::OnID_LogWindow)
+ EVT_MENU(wxID_EXIT, AppFrame::OnExitEvt)
EVT_MENU(wxID_ABOUT, AppFrame::OnAbout)
EVT_TIMER(AppFrame::ID_Timer, AppFrame::OnID_Timer)