rockbox/utils/regtools/qeditor/regtab.cpp
Amaury Pouly 0e0c610df0 utils/regtools: make qeditor able to poke directly at a hwstub device
This commit add the very handy feature of being able to read registers
directly from a device using hwstub. This is mostly trivial using the hwstub
library and the biggest change here is actually:
- being able to read registers by name and/or addresses
- being able to enumerate devives
The UI code currently doesn't handle hotplug but the backend does so it should
be trivial to add in the future. It also opens up the possibility the write
registers from hwstub or save the register values to a file.
Since it relies on both hwstub and libusb, a switch has been introduced in
qmake to disable it (use -config nohwstub).

Change-Id: I5d7d7a2a7c97ecd7407227357c8553c2773ea6cc
2014-02-10 23:14:25 +01:00

453 lines
15 KiB
C++

#include "regtab.h"
#include <QSplitter>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QAbstractListModel>
#include <QMessageBox>
#include <QSizePolicy>
#include <QHBoxLayout>
#include <QStringBuilder>
#include <QLabel>
#include <QGridLayout>
#include <QTableWidget>
#include <QHeaderView>
#include <QFileDialog>
#include <QDebug>
#include "backend.h"
#include "analyser.h"
RegTreeItem::RegTreeItem(const QString& string, int type)
:QTreeWidgetItem(QStringList(string), type)
{
}
void RegTreeItem::SetPath(int dev_idx, int dev_addr_idx, int reg_idx, int reg_addr_idx)
{
m_dev_idx = dev_idx;
m_dev_addr_idx = dev_addr_idx;
m_reg_idx = reg_idx;
m_reg_addr_idx = reg_addr_idx;
}
RegTab::RegTab(Backend *backend, QTabWidget *parent)
:m_backend(backend)
{
m_splitter = new QSplitter();
QWidget *left = new QWidget;
m_splitter->addWidget(left);
m_splitter->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("face-sad"), "None", 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_data_soc_label = new QLabel;
QPushButton *data_sel_reload = new QPushButton;
data_sel_reload->setIcon(QIcon::fromTheme("view-refresh"));
data_sel_layout->addWidget(m_data_selector);
data_sel_layout->addWidget(m_data_sel_edit);
#ifdef HAVE_HWSTUB
m_dev_selector = new QComboBox;
data_sel_layout->addWidget(m_dev_selector, 1);
#endif
data_sel_layout->addWidget(m_data_soc_label);
data_sel_layout->addWidget(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);
m_right_content = new QWidget;
QVBoxLayout *l = new QVBoxLayout;
l->addStretch();
m_right_content->setLayout(l);
m_right_panel->addWidget(m_right_content);
QWidget *w = new QWidget;
w->setLayout(m_right_panel);
m_splitter->addWidget(w);
m_splitter->setStretchFactor(1, 2);
m_io_backend = m_backend->CreateDummyIoBackend();
parent->addTab(m_splitter, "Register Tab");
connect(m_soc_selector, SIGNAL(currentIndexChanged(const QString&)),
this, SLOT(OnSocChanged(const QString&)));
connect(m_backend, SIGNAL(OnSocListChanged()), this, SLOT(OnSocListChanged()));
connect(m_reg_tree, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
this, SLOT(OnRegItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
connect(m_reg_tree, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this,
SLOT(OnRegItemClicked(QTreeWidgetItem *, int)));
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 *)));
connect(m_analysers_list, SIGNAL(itemClicked(QListWidgetItem *)), this,
SLOT(OnAnalyserClicked(QListWidgetItem *)));
#ifdef HAVE_HWSTUB
connect(m_dev_selector, SIGNAL(currentIndexChanged(int)),
this, SLOT(OnDevChanged(int)));
#endif
OnSocListChanged();
OnDataSelChanged(DataSelNothing);
}
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
QFileDialog *fd = new QFileDialog(m_data_selector);
fd->setFilter("Textual files (*.txt);;All files (*)");
fd->setDirectory(Settings::Get()->value("regtab/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("regtab/loaddatadir", fd->directory().absolutePath());
}
#ifdef HAVE_HWSTUB
else if(var == DataSelDevice)
{
m_data_sel_edit->hide();
m_dev_selector->show();
OnDevListChanged();
}
#endif
else
{
m_data_sel_edit->show();
#ifdef HAVE_HWSTUB
m_dev_selector->hide();
#endif
delete m_io_backend;
m_io_backend = m_backend->CreateDummyIoBackend();
SetDataSocName("");
}
OnDataChanged();
}
void RegTab::OnDataChanged()
{
OnRegItemChanged(m_reg_tree->currentItem(), m_reg_tree->currentItem());
}
void RegTab::OnRegItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
(void) previous;
OnRegItemClicked(current, 0);
}
void RegTab::OnRegItemClicked(QTreeWidgetItem *current, int col)
{
(void) col;
if(current == 0)
return;
RegTreeItem *item = dynamic_cast< RegTreeItem * >(current);
if(item->type() != RegTreeRegType)
return;
soc_dev_t& dev = m_cur_soc.dev[item->GetDevIndex()];
soc_dev_addr_t& dev_addr = dev.addr[item->GetDevAddrIndex()];
soc_reg_t& reg = dev.reg[item->GetRegIndex()];
soc_reg_addr_t& reg_addr = reg.addr[item->GetRegAddrIndex()];
DisplayRegister(dev, dev_addr, reg, reg_addr);
}
void RegTab::OnAnalyserChanged(QListWidgetItem *current, QListWidgetItem *previous)
{
(void) previous;
OnAnalyserClicked(current);
}
void RegTab::OnAnalyserClicked(QListWidgetItem *current)
{
if(current == 0)
return;
delete m_right_content;
AnalyserFactory *ana = AnalyserFactory::GetAnalyserByName(current->text());
m_right_content = ana->Create(m_cur_soc, m_io_backend)->GetWidget();
m_right_panel->addWidget(m_right_content, 1);
}
void RegTab::DisplayRegister(soc_dev_t& dev, soc_dev_addr_t& dev_addr,
soc_reg_t& reg, soc_reg_addr_t& reg_addr)
{
(void) dev;
delete m_right_content;
QVBoxLayout *right_layout = new QVBoxLayout;
QString reg_name;
reg_name.sprintf("HW_%s_%s", dev_addr.name.c_str(), reg_addr.name.c_str());
QStringList names;
QVector< soc_addr_t > addresses;
names.append(reg_name);
addresses.append(reg_addr.addr);
if(reg.flags & REG_HAS_SCT)
{
names.append(reg_name + "_SET");
names.append(reg_name + "_CLR");
names.append(reg_name + "_TOG");
addresses.append(reg_addr.addr + 4);
addresses.append(reg_addr.addr + 8);
addresses.append(reg_addr.addr + 12);
}
QString str;
str += "<table align=left>";
for(int i = 0; i < names.size(); i++)
str += "<tr><td><b>" + names[i] + "</b></td></tr>";
str += "</table>";
QLabel *label_names = new QLabel;
label_names->setTextFormat(Qt::RichText);
label_names->setText(str);
QString str_addr;
str_addr += "<table align=left>";
for(int i = 0; i < names.size(); i++)
str_addr += "<tr><td><b>" + QString().sprintf("0x%03x", addresses[i]) + "</b></td></tr>";
str_addr += "</table>";
QLabel *label_addr = new QLabel;
label_addr->setTextFormat(Qt::RichText);
label_addr->setText(str_addr);
QHBoxLayout *top_layout = new QHBoxLayout;
top_layout->addStretch();
top_layout->addWidget(label_names);
top_layout->addWidget(label_addr);
top_layout->addStretch();
soc_word_t value;
BackendHelper helper(m_io_backend, m_cur_soc);
bool has_value = helper.ReadRegister(dev_addr.name.c_str(), reg_addr.name.c_str(), value);
QHBoxLayout *raw_val_layout = 0;
if(has_value)
{
QLabel *raw_val_name = new QLabel;
raw_val_name->setText("Raw value:");
QLineEdit *raw_val_edit = new QLineEdit;
raw_val_edit->setReadOnly(true);
raw_val_edit->setText(QString().sprintf("0x%08x", value));
raw_val_edit->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
raw_val_layout = new QHBoxLayout;
raw_val_layout->addStretch();
raw_val_layout->addWidget(raw_val_name);
raw_val_layout->addWidget(raw_val_edit);
raw_val_layout->addStretch();
}
QTableWidget *value_table = new QTableWidget;
value_table->setRowCount(reg.field.size());
value_table->setColumnCount(4);
for(size_t i = 0; i < reg.field.size(); i++)
{
QString bits_str;
if(reg.field[i].first_bit == reg.field[i].last_bit)
bits_str.sprintf("%d", reg.field[i].first_bit);
else
bits_str.sprintf("%d:%d", reg.field[i].last_bit, reg.field[i].first_bit);
QTableWidgetItem *item = new QTableWidgetItem(bits_str);
item->setTextAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
value_table->setItem(i, 0, item);
item = new QTableWidgetItem(QString(reg.field[i].name.c_str()));
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
value_table->setItem(i, 1, item);
item = new QTableWidgetItem();
if(has_value)
{
const soc_reg_field_t& field = reg.field[i];
soc_word_t v = (value & field.bitmask()) >> field.first_bit;
QString value_name;
for(size_t j = 0; j < field.value.size(); j++)
if(v == field.value[j].value)
value_name = field.value[j].name.c_str();
const char *fmt = "%lu";
// heuristic
if((field.last_bit - field.first_bit + 1) > 16)
fmt = "0x%lx";
item->setText(QString().sprintf(fmt, (unsigned long)v));
item->setTextAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
if(value_name.size() != 0)
{
QTableWidgetItem *t = new QTableWidgetItem(value_name);
t->setTextAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
t->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
value_table->setItem(i, 3, t);
}
}
item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
value_table->setItem(i, 2, item);
}
value_table->setHorizontalHeaderItem(0, new QTableWidgetItem("Bits"));
value_table->setHorizontalHeaderItem(1, new QTableWidgetItem("Name"));
value_table->setHorizontalHeaderItem(2, new QTableWidgetItem("Value"));
value_table->setHorizontalHeaderItem(3, new QTableWidgetItem("Meaning"));
value_table->verticalHeader()->setVisible(false);
value_table->resizeColumnsToContents();
value_table->horizontalHeader()->setStretchLastSection(true);
value_table->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
right_layout->addLayout(top_layout);
if(raw_val_layout)
right_layout->addLayout(raw_val_layout);
//right_layout->addWidget(bits_label);
right_layout->addWidget(value_table);
//right_layout->addStretch();
m_right_content = new QGroupBox("Register Description");
m_right_content->setLayout(right_layout);
m_right_panel->addWidget(m_right_content);
}
void RegTab::OnSocListChanged()
{
m_soc_selector->clear();
QStringList socs = m_backend->GetSocNameList();
for(int i = 0; i < socs.size(); i++)
m_soc_selector->addItem(socs[i]);
}
#ifdef HAVE_HWSTUB
void RegTab::OnDevListChanged()
{
m_dev_selector->clear();
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("");
}
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;
m_io_backend = m_backend->CreateHWStubIoBackend(dev);
SetDataSocName(m_io_backend->GetSocName());
OnDataSocActivated(m_io_backend->GetSocName());
OnDataChanged();
}
#endif
void RegTab::FillDevSubTree(RegTreeItem *item)
{
soc_dev_t& sd = m_cur_soc.dev[item->GetDevIndex()];
for(size_t i = 0; i < sd.reg.size(); i++)
{
soc_reg_t& reg = sd.reg[i];
for(size_t j = 0; j < reg.addr.size(); j++)
{
RegTreeItem *reg_item = new RegTreeItem(reg.addr[j].name.c_str(), RegTreeRegType);
reg_item->SetPath(item->GetDevIndex(), item->GetDevAddrIndex(), i, j);
item->addChild(reg_item);
}
}
}
void RegTab::FillRegTree()
{
for(size_t i = 0; i < m_cur_soc.dev.size(); i++)
{
soc_dev_t& sd = m_cur_soc.dev[i];
for(size_t j = 0; j < sd.addr.size(); j++)
{
RegTreeItem *dev_item = new RegTreeItem(sd.addr[j].name.c_str(), RegTreeDevType);
dev_item->SetPath(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.name.c_str()));
}
void RegTab::OnSocChanged(const QString& soc)
{
m_reg_tree->clear();
if(!m_backend->GetSocByName(soc, m_cur_soc))
return;
FillRegTree();
FillAnalyserList();
}