7b590a9536
Qeditor has been improved in many ways: - it can now dump all registers, just like the lua DUMPER.dump_all() command - several crash were fixed - when connected to a hwstub command, one can correctly edit individual fields - the code was simplified in several places Change-Id: I092b99ce3a12ff6417552de61d62c65f706bcff0
470 lines
13 KiB
C++
470 lines
13 KiB
C++
#include "regtab.h"
|
|
|
|
#include <QSizePolicy>
|
|
#include <QStringBuilder>
|
|
#include <QFileDialog>
|
|
#include <QDebug>
|
|
#include <QStyle>
|
|
#include <QMessageBox>
|
|
#include "backend.h"
|
|
#include "analyser.h"
|
|
#include "regdisplaypanel.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
enum
|
|
{
|
|
RegTreeDevType = QTreeWidgetItem::UserType,
|
|
RegTreeRegType
|
|
};
|
|
|
|
class DevTreeItem : public QTreeWidgetItem
|
|
{
|
|
public:
|
|
DevTreeItem(const QString& string, const SocDevRef& ref)
|
|
:QTreeWidgetItem(QStringList(string), RegTreeDevType), m_ref(ref) {}
|
|
|
|
const SocDevRef& GetRef() { return m_ref; }
|
|
private:
|
|
SocDevRef m_ref;
|
|
};
|
|
|
|
class RegTreeItem : public QTreeWidgetItem
|
|
{
|
|
public:
|
|
RegTreeItem(const QString& string, const SocRegRef& ref)
|
|
:QTreeWidgetItem(QStringList(string), RegTreeRegType), m_ref(ref) {}
|
|
|
|
const SocRegRef& GetRef() { return m_ref; }
|
|
private:
|
|
SocRegRef m_ref;
|
|
};
|
|
|
|
}
|
|
|
|
/**
|
|
* EmptyRegTabPanel
|
|
*/
|
|
EmptyRegTabPanel::EmptyRegTabPanel(QWidget *parent)
|
|
:QWidget(parent)
|
|
{
|
|
QVBoxLayout *l = new QVBoxLayout;
|
|
l->addStretch();
|
|
setLayout(l);
|
|
}
|
|
|
|
void EmptyRegTabPanel::AllowWrite(bool en)
|
|
{
|
|
Q_UNUSED(en);
|
|
}
|
|
|
|
QWidget *EmptyRegTabPanel::GetWidget()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
|
|
/**
|
|
* RegTab
|
|
*/
|
|
|
|
RegTab::RegTab(Backend *backend, QWidget *parent)
|
|
:QSplitter(parent), m_backend(backend)
|
|
{
|
|
QWidget *left = new QWidget;
|
|
this->addWidget(left);
|
|
this->setStretchFactor(0, 1);
|
|
QVBoxLayout *left_layout = new QVBoxLayout;
|
|
left->setLayout(left_layout);
|
|
|
|
QGroupBox *top_group = new QGroupBox("SOC selection");
|
|
QVBoxLayout *top_group_layout = new QVBoxLayout;
|
|
m_soc_selector = new QComboBox;
|
|
top_group_layout->addWidget(m_soc_selector);
|
|
top_group->setLayout(top_group_layout);
|
|
|
|
m_reg_tree = new QTreeWidget();
|
|
m_reg_tree->setColumnCount(1);
|
|
m_reg_tree->setHeaderLabel(QString("Name"));
|
|
|
|
m_analysers_list = new QListWidget;
|
|
|
|
m_type_selector = new QTabWidget;
|
|
m_type_selector->addTab(m_reg_tree, "Registers");
|
|
m_type_selector->addTab(m_analysers_list, "Analyzers");
|
|
m_type_selector->setTabPosition(QTabWidget::West);
|
|
|
|
left_layout->addWidget(top_group);
|
|
left_layout->addWidget(m_type_selector);
|
|
|
|
m_right_panel = new QVBoxLayout;
|
|
QGroupBox *data_sel_group = new QGroupBox("Data selection");
|
|
QHBoxLayout *data_sel_layout = new QHBoxLayout;
|
|
m_data_selector = new QComboBox;
|
|
m_data_selector->addItem(QIcon::fromTheme("text-x-generic"), "Explore", QVariant(DataSelNothing));
|
|
m_data_selector->addItem(QIcon::fromTheme("document-open"), "File...", QVariant(DataSelFile));
|
|
#ifdef HAVE_HWSTUB
|
|
m_data_selector->addItem(QIcon::fromTheme("multimedia-player"), "Device...", QVariant(DataSelDevice));
|
|
#endif
|
|
m_data_sel_edit = new QLineEdit;
|
|
m_data_sel_edit->setReadOnly(true);
|
|
m_readonly_check = new QCheckBox("Read-only");
|
|
m_readonly_check->setCheckState(Qt::Checked);
|
|
m_data_soc_label = new QLabel;
|
|
m_dump = new QPushButton("Dump", this);
|
|
m_dump->setIcon(QIcon::fromTheme("system-run"));
|
|
m_data_sel_reload = new QPushButton(this);
|
|
m_data_sel_reload->setIcon(QIcon::fromTheme("view-refresh"));
|
|
m_data_sel_reload->setToolTip("Reload data");
|
|
data_sel_layout->addWidget(m_data_selector);
|
|
data_sel_layout->addWidget(m_data_sel_edit, 1);
|
|
data_sel_layout->addStretch(0);
|
|
#ifdef HAVE_HWSTUB
|
|
m_dev_selector = new QComboBox;
|
|
data_sel_layout->addWidget(m_dev_selector, 1);
|
|
#endif
|
|
data_sel_layout->addWidget(m_readonly_check);
|
|
data_sel_layout->addWidget(m_data_soc_label);
|
|
data_sel_layout->addWidget(m_dump);
|
|
data_sel_layout->addWidget(m_data_sel_reload);
|
|
data_sel_group->setLayout(data_sel_layout);
|
|
m_data_soc_label->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
|
|
|
|
m_right_panel->addWidget(data_sel_group, 0);
|
|
m_right_content = 0;
|
|
SetPanel(new EmptyRegTabPanel);
|
|
QWidget *w = new QWidget;
|
|
w->setLayout(m_right_panel);
|
|
this->addWidget(w);
|
|
this->setStretchFactor(1, 2);
|
|
|
|
m_io_backend = m_backend->CreateDummyIoBackend();
|
|
|
|
connect(m_soc_selector, SIGNAL(currentIndexChanged(int)),
|
|
this, SLOT(OnSocChanged(int)));
|
|
connect(m_backend, SIGNAL(OnSocListChanged()), this, SLOT(OnSocListChanged()));
|
|
connect(m_reg_tree, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
|
|
this, SLOT(OnRegItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
|
|
connect(m_data_selector, SIGNAL(activated(int)),
|
|
this, SLOT(OnDataSelChanged(int)));
|
|
connect(m_data_soc_label, SIGNAL(linkActivated(const QString&)), this,
|
|
SLOT(OnDataSocActivated(const QString&)));
|
|
connect(m_analysers_list, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
|
|
this, SLOT(OnAnalyserChanged(QListWidgetItem *, QListWidgetItem *)));
|
|
#ifdef HAVE_HWSTUB
|
|
connect(m_dev_selector, SIGNAL(currentIndexChanged(int)),
|
|
this, SLOT(OnDevChanged(int)));
|
|
#endif
|
|
connect(m_readonly_check, SIGNAL(clicked(bool)), this, SLOT(OnReadOnlyClicked(bool)));
|
|
connect(m_dump, SIGNAL(clicked(bool)), this, SLOT(OnDumpRegs(bool)));
|
|
|
|
OnSocListChanged();
|
|
OnDataSelChanged(0);
|
|
}
|
|
|
|
RegTab::~RegTab()
|
|
{
|
|
#ifdef HAVE_HWSTUB
|
|
ClearDevList();
|
|
#endif
|
|
delete m_io_backend;
|
|
}
|
|
|
|
bool RegTab::Quit()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void RegTab::SetDataSocName(const QString& socname)
|
|
{
|
|
if(socname.size() != 0)
|
|
{
|
|
m_data_soc_label->setText("<a href=\"" + socname + "\">" + socname + "</a>");
|
|
m_data_soc_label->setTextFormat(Qt::RichText);
|
|
m_data_soc_label->show();
|
|
}
|
|
else
|
|
{
|
|
m_data_soc_label->setText("");
|
|
m_data_soc_label->hide();
|
|
}
|
|
}
|
|
|
|
void RegTab::OnDataSocActivated(const QString& str)
|
|
{
|
|
int index = m_soc_selector->findText(str);
|
|
if(index != -1)
|
|
m_soc_selector->setCurrentIndex(index);
|
|
}
|
|
|
|
void RegTab::OnDataSelChanged(int index)
|
|
{
|
|
if(index == -1)
|
|
return;
|
|
QVariant var = m_data_selector->itemData(index);
|
|
if(var == DataSelFile)
|
|
{
|
|
m_data_sel_edit->show();
|
|
#ifdef HAVE_HWSTUB
|
|
m_dev_selector->hide();
|
|
#endif
|
|
m_readonly_check->show();
|
|
m_data_sel_reload->show();
|
|
m_dump->hide();
|
|
QFileDialog *fd = new QFileDialog(m_data_selector);
|
|
fd->setFilter("Textual files (*.txt);;All files (*)");
|
|
fd->setDirectory(Settings::Get()->value("loaddatadir", QDir::currentPath()).toString());
|
|
if(fd->exec())
|
|
{
|
|
QStringList filenames = fd->selectedFiles();
|
|
delete m_io_backend;
|
|
m_io_backend = m_backend->CreateFileIoBackend(filenames[0]);
|
|
m_data_sel_edit->setText(filenames[0]);
|
|
SetDataSocName(m_io_backend->GetSocName());
|
|
OnDataSocActivated(m_io_backend->GetSocName());
|
|
}
|
|
Settings::Get()->setValue("loaddatadir", fd->directory().absolutePath());
|
|
SetReadOnlyIndicator();
|
|
}
|
|
#ifdef HAVE_HWSTUB
|
|
else if(var == DataSelDevice)
|
|
{
|
|
m_data_sel_edit->hide();
|
|
m_readonly_check->show();
|
|
m_dev_selector->show();
|
|
m_data_sel_reload->hide();
|
|
m_dump->show();
|
|
OnDevListChanged();
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
m_data_sel_edit->show();
|
|
#ifdef HAVE_HWSTUB
|
|
m_dev_selector->hide();
|
|
#endif
|
|
m_readonly_check->hide();
|
|
m_data_sel_reload->hide();
|
|
m_dump->hide();
|
|
|
|
delete m_io_backend;
|
|
m_io_backend = m_backend->CreateDummyIoBackend();
|
|
m_readonly_check->setCheckState(Qt::Checked);
|
|
SetDataSocName("");
|
|
UpdateSocFilename();
|
|
}
|
|
OnDataChanged();
|
|
}
|
|
|
|
void RegTab::UpdateSocFilename()
|
|
{
|
|
int index = m_data_selector->currentIndex();
|
|
if(index == -1)
|
|
return;
|
|
if(m_data_selector->itemData(index) != DataSelNothing)
|
|
return;
|
|
index = m_soc_selector->currentIndex();
|
|
if(index == -1)
|
|
return;
|
|
SocRef ref = m_soc_selector->itemData(index).value< SocRef >();
|
|
m_data_sel_edit->setText(ref.GetSocFile()->GetFilename());
|
|
}
|
|
|
|
void RegTab::SetReadOnlyIndicator()
|
|
{
|
|
if(m_io_backend->IsReadOnly())
|
|
m_readonly_check->setCheckState(Qt::Checked);
|
|
}
|
|
|
|
void RegTab::OnDataChanged()
|
|
{
|
|
OnRegItemChanged(m_reg_tree->currentItem(), m_reg_tree->currentItem());
|
|
}
|
|
|
|
void RegTab::OnRegItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
|
|
{
|
|
Q_UNUSED(previous);
|
|
OnRegItemClicked(current, 0);
|
|
}
|
|
|
|
void RegTab::OnRegItemClicked(QTreeWidgetItem *current, int col)
|
|
{
|
|
Q_UNUSED(col);
|
|
if(current == 0)
|
|
return;
|
|
if(current->type() == RegTreeRegType)
|
|
{
|
|
RegTreeItem *item = dynamic_cast< RegTreeItem * >(current);
|
|
DisplayRegister(item->GetRef());
|
|
}
|
|
else if(current->type() == RegTreeDevType)
|
|
{
|
|
DevTreeItem *item = dynamic_cast< DevTreeItem * >(current);
|
|
DisplayDevice(item->GetRef());
|
|
}
|
|
}
|
|
|
|
void RegTab::OnAnalyserChanged(QListWidgetItem *current, QListWidgetItem *previous)
|
|
{
|
|
Q_UNUSED(previous);
|
|
OnAnalyserClicked(current);
|
|
}
|
|
|
|
void RegTab::OnAnalyserClicked(QListWidgetItem *current)
|
|
{
|
|
if(current == 0)
|
|
return;
|
|
AnalyserFactory *ana = AnalyserFactory::GetAnalyserByName(current->text());
|
|
SetPanel(ana->Create(m_cur_soc, m_io_backend));
|
|
}
|
|
|
|
void RegTab::DisplayRegister(const SocRegRef& ref)
|
|
{
|
|
SetPanel(new RegDisplayPanel(this, m_io_backend, ref));
|
|
}
|
|
|
|
void RegTab::DisplayDevice(const SocDevRef& ref)
|
|
{
|
|
SetPanel(new DevDisplayPanel(this, ref));
|
|
}
|
|
|
|
void RegTab::SetPanel(RegTabPanel *panel)
|
|
{
|
|
delete m_right_content;
|
|
m_right_content = panel;
|
|
m_right_content->AllowWrite(m_readonly_check->checkState() == Qt::Unchecked);
|
|
m_right_panel->addWidget(m_right_content->GetWidget(), 1);
|
|
}
|
|
|
|
void RegTab::OnSocListChanged()
|
|
{
|
|
m_soc_selector->clear();
|
|
QList< SocRef > socs = m_backend->GetSocList();
|
|
for(int i = 0; i < socs.size(); i++)
|
|
{
|
|
QVariant v;
|
|
v.setValue(socs[i]);
|
|
m_soc_selector->addItem(QString::fromStdString(socs[i].GetSoc().name), v);
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_HWSTUB
|
|
void RegTab::OnDevListChanged()
|
|
{
|
|
ClearDevList();
|
|
QList< HWStubDevice* > list = m_hwstub_helper.GetDevList();
|
|
foreach(HWStubDevice *dev, list)
|
|
{
|
|
QString name = QString("Bus %1 Device %2: %3").arg(dev->GetBusNumber())
|
|
.arg(dev->GetDevAddress()).arg(dev->GetTargetInfo().bName);
|
|
m_dev_selector->addItem(QIcon::fromTheme("multimedia-player"), name,
|
|
QVariant::fromValue((void *)dev));
|
|
}
|
|
if(list.size() > 0)
|
|
m_dev_selector->setCurrentIndex(0);
|
|
else
|
|
SetDataSocName("");
|
|
SetReadOnlyIndicator();
|
|
}
|
|
|
|
void RegTab::OnDevChanged(int index)
|
|
{
|
|
if(index == -1)
|
|
return;
|
|
HWStubDevice *dev = reinterpret_cast< HWStubDevice* >(m_dev_selector->itemData(index).value< void* >());
|
|
delete m_io_backend;
|
|
/* NOTE: make a copy of the HWStubDevice device because the one in the list
|
|
* might get destroyed when clearing the list while the backend is still
|
|
* active: this would result in a double free when the backend is also destroyed */
|
|
m_io_backend = m_backend->CreateHWStubIoBackend(new HWStubDevice(dev));
|
|
SetDataSocName(m_io_backend->GetSocName());
|
|
OnDataSocActivated(m_io_backend->GetSocName());
|
|
OnDataChanged();
|
|
}
|
|
|
|
void RegTab::ClearDevList()
|
|
{
|
|
while(m_dev_selector->count() > 0)
|
|
{
|
|
HWStubDevice *dev = reinterpret_cast< HWStubDevice* >(m_dev_selector->itemData(0).value< void* >());
|
|
delete dev;
|
|
m_dev_selector->removeItem(0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void RegTab::FillDevSubTree(QTreeWidgetItem *_item)
|
|
{
|
|
DevTreeItem *item = dynamic_cast< DevTreeItem* >(_item);
|
|
const soc_dev_t& dev = item->GetRef().GetDev();
|
|
for(size_t i = 0; i < dev.reg.size(); i++)
|
|
{
|
|
const soc_reg_t& reg = dev.reg[i];
|
|
for(size_t j = 0; j < reg.addr.size(); j++)
|
|
{
|
|
RegTreeItem *reg_item = new RegTreeItem(reg.addr[j].name.c_str(),
|
|
SocRegRef(item->GetRef(), i, j));
|
|
item->addChild(reg_item);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RegTab::FillRegTree()
|
|
{
|
|
for(size_t i = 0; i < m_cur_soc.GetSoc().dev.size(); i++)
|
|
{
|
|
const soc_dev_t& dev = m_cur_soc.GetSoc().dev[i];
|
|
for(size_t j = 0; j < dev.addr.size(); j++)
|
|
{
|
|
DevTreeItem *dev_item = new DevTreeItem(dev.addr[j].name.c_str(),
|
|
SocDevRef(m_cur_soc, i, j));
|
|
FillDevSubTree(dev_item);
|
|
m_reg_tree->addTopLevelItem(dev_item);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RegTab::FillAnalyserList()
|
|
{
|
|
m_analysers_list->clear();
|
|
m_analysers_list->addItems(AnalyserFactory::GetAnalysersForSoc(m_cur_soc.GetSoc().name.c_str()));
|
|
}
|
|
|
|
void RegTab::OnSocChanged(int index)
|
|
{
|
|
if(index == -1)
|
|
return;
|
|
m_reg_tree->clear();
|
|
m_cur_soc = m_soc_selector->itemData(index).value< SocRef >();
|
|
FillRegTree();
|
|
FillAnalyserList();
|
|
UpdateSocFilename();
|
|
}
|
|
|
|
void RegTab::OnReadOnlyClicked(bool checked)
|
|
{
|
|
if(m_io_backend->IsReadOnly())
|
|
return SetReadOnlyIndicator();
|
|
m_right_content->AllowWrite(!checked);
|
|
UpdateSocFilename();
|
|
}
|
|
|
|
void RegTab::OnDumpRegs(bool c)
|
|
{
|
|
Q_UNUSED(c);
|
|
QFileDialog *fd = new QFileDialog(this);
|
|
fd->setAcceptMode(QFileDialog::AcceptSave);
|
|
fd->setFilter("Textual files (*.txt);;All files (*)");
|
|
fd->setDirectory(Settings::Get()->value("loaddatadir", QDir::currentPath()).toString());
|
|
if(!fd->exec())
|
|
return;
|
|
QStringList filenames = fd->selectedFiles();
|
|
Settings::Get()->setValue("loaddatadir", fd->directory().absolutePath());
|
|
BackendHelper bh(m_io_backend, m_cur_soc);
|
|
if(!bh.DumpAllRegisters(filenames[0]))
|
|
{
|
|
QMessageBox::warning(this, "The register dump was not saved",
|
|
"There was an error when dumping the registers");
|
|
}
|
|
}
|