rockbox/utils/regtools/qeditor/backend.cpp
Amaury Pouly 1cada1f833 soc_desc: new version of the desc file format
Fix qeditor to use the old soc_desc_v1.
Port hwstub_shell to the new description format.

Change-Id: I9fefbff534bfaa5c3603bb3dd8307a2b76e88cfc
2015-09-11 16:40:19 +02:00

632 lines
15 KiB
C++

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2014 by Amaury Pouly
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QFileInfo>
#include "backend.h"
/**
* SocFile
*/
SocFile::SocFile()
:m_valid(false)
{
}
SocFile::SocFile(const QString& filename)
:m_filename(filename)
{
m_valid = parse_xml(filename.toStdString(), m_soc);
normalize(m_soc);
}
bool SocFile::IsValid()
{
return m_valid;
}
SocRef SocFile::GetSocRef()
{
return SocRef(this);
}
QString SocFile::GetFilename()
{
return m_filename;
}
/**
* Backend
*/
Backend::Backend()
{
}
QList< SocFileRef > Backend::GetSocFileList()
{
QList< SocFileRef > list;
for(std::list< SocFile >::iterator it = m_socs.begin(); it != m_socs.end(); ++it)
list.append(SocFileRef(&(*it)));
return list;
}
QList< SocRef > Backend::GetSocList()
{
QList< SocRef > list;
for(std::list< SocFile >::iterator it = m_socs.begin(); it != m_socs.end(); ++it)
list.append(it->GetSocRef());
return list;
}
bool Backend::LoadSocDesc(const QString& filename)
{
SocFile f(filename);
if(!f.IsValid())
return false;
m_socs.push_back(f);
emit OnSocListChanged();
return true;
}
IoBackend *Backend::CreateFileIoBackend(const QString& filename)
{
return new FileIoBackend(filename);
}
IoBackend *Backend::CreateDummyIoBackend()
{
return new DummyIoBackend();
}
#ifdef HAVE_HWSTUB
IoBackend *Backend::CreateHWStubIoBackend(HWStubDevice *dev)
{
return new HWStubIoBackend(dev);
}
#endif
/**
* RamIoBackend
*/
RamIoBackend::RamIoBackend(const QString& soc_name)
{
m_soc = soc_name;
}
bool RamIoBackend::ReadRegister(const QString& name, soc_word_t& value)
{
QMap<QString, soc_word_t>::const_iterator it = m_map.find(name);
if(it == m_map.end())
return false;
value = it.value();
return true;
}
void RamIoBackend::DeleteAll()
{
m_map.clear();
}
bool RamIoBackend::WriteRegister(const QString& name, soc_word_t value, WriteMode mode)
{
switch(mode)
{
case Write: m_map[name] = value; return true;
case Set: m_map[name] |= value; return true;
case Clear: m_map[name] &= ~value; return true;
case Toggle: m_map[name] ^= value; return true;
default: return false;
}
}
/**
* FileIoBackend
*/
FileIoBackend::FileIoBackend(const QString& filename, const QString& soc_name)
:RamIoBackend(soc_name)
{
m_filename = filename;
m_valid = false;
Reload();
}
bool FileIoBackend::Reload()
{
m_valid = false;
QFile file(m_filename);
if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
return false;
DeleteAll();
QTextStream in(&file);
while(!in.atEnd())
{
QString line = in.readLine();
int idx = line.indexOf('=');
if(idx == -1)
continue;
QString key = line.left(idx).trimmed();
bool ok;
soc_word_t val = line.mid(idx + 1).trimmed().toULong(&ok, 0);
if(key == "HW")
m_soc = line.mid(idx + 1).trimmed();
else if(ok)
RamIoBackend::WriteRegister(key, val, Write);
}
m_readonly = !QFileInfo(file).isWritable();
m_dirty = false;
m_valid = true;
return true;
}
bool FileIoBackend::WriteRegister(const QString& name, soc_word_t value, WriteMode mode)
{
m_dirty = true;
return RamIoBackend::WriteRegister(name, value, mode);
}
bool FileIoBackend::Commit()
{
if(!m_dirty)
return true;
QFile file(m_filename);
if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
return false;
QTextStream out(&file);
out << "HW = " << m_soc << "\n";
QMapIterator< QString, soc_word_t > it(m_map);
while(it.hasNext())
{
it.next();
out << it.key() << " = " << hex << showbase << it.value() << "\n";
}
out.flush();
return file.flush();
}
#ifdef HAVE_HWSTUB
/**
* HWStubDevice
*/
HWStubDevice::HWStubDevice(struct libusb_device *dev)
{
Init(dev);
}
HWStubDevice::HWStubDevice(const HWStubDevice *dev)
{
Init(dev->m_dev);
}
void HWStubDevice::Init(struct libusb_device *dev)
{
libusb_ref_device(dev);
m_dev = dev;
m_handle = 0;
m_hwdev = 0;
m_valid = Probe();
}
HWStubDevice::~HWStubDevice()
{
Close();
libusb_unref_device(m_dev);
}
int HWStubDevice::GetBusNumber()
{
return libusb_get_bus_number(m_dev);
}
int HWStubDevice::GetDevAddress()
{
return libusb_get_device_address(m_dev);
}
bool HWStubDevice::Probe()
{
if(!Open())
return false;
// get target
int ret = hwstub_get_desc(m_hwdev, HWSTUB_DT_TARGET, &m_hwdev_target, sizeof(m_hwdev_target));
if(ret != sizeof(m_hwdev_target))
goto Lerr;
// get STMP information
if(m_hwdev_target.dID == HWSTUB_TARGET_STMP)
{
ret = hwstub_get_desc(m_hwdev, HWSTUB_DT_STMP, &m_hwdev_stmp, sizeof(m_hwdev_stmp));
if(ret != sizeof(m_hwdev_stmp))
goto Lerr;
}
else if(m_hwdev_target.dID == HWSTUB_TARGET_PP)
{
ret = hwstub_get_desc(m_hwdev, HWSTUB_DT_PP, &m_hwdev_pp, sizeof(m_hwdev_pp));
if(ret != sizeof(m_hwdev_pp))
goto Lerr;
}
Close();
return true;
Lerr:
Close();
return false;
}
bool HWStubDevice::Open()
{
if(libusb_open(m_dev, &m_handle))
return false;
m_hwdev = hwstub_open(m_handle);
if(m_hwdev == 0)
{
libusb_close(m_handle);
m_handle = 0;
return false;
}
return true;
}
void HWStubDevice::Close()
{
if(m_hwdev)
hwstub_release(m_hwdev);
m_hwdev = 0;
if(m_handle)
libusb_close(m_handle);
m_handle = 0;
}
bool HWStubDevice::ReadMem(soc_addr_t addr, size_t length, void *buffer)
{
if(!m_hwdev)
return false;
int ret = hwstub_rw_mem_atomic(m_hwdev, 1, addr, buffer, length);
return ret >= 0 && (size_t)ret == length;
}
bool HWStubDevice::WriteMem(soc_addr_t addr, size_t length, void *buffer)
{
if(!m_hwdev)
return false;
int ret = hwstub_rw_mem_atomic(m_hwdev, 0, addr, buffer, length);
return ret >= 0 && (size_t)ret == length;
}
bool HWStubDevice::IsValid()
{
return m_valid;
}
/**
* HWStubIoBackend
*/
HWStubIoBackend::HWStubIoBackend(HWStubDevice *dev)
{
m_dev = dev;
m_dev->Open();
struct hwstub_target_desc_t target = m_dev->GetTargetInfo();
if(target.dID == HWSTUB_TARGET_STMP)
{
struct hwstub_stmp_desc_t stmp = m_dev->GetSTMPInfo();
if(stmp.wChipID == 0x3780)
m_soc = "imx233";
else if(stmp.wChipID >= 0x3700 && stmp.wChipID < 0x3780)
m_soc = "stmp3700";
else if(stmp.wChipID >= 0x3600 && stmp.wChipID < 0x3700)
m_soc = "stmp3600";
else
m_soc = QString("stmp%1").arg(stmp.wChipID, 4, 16, QChar('0'));
}
else if(target.dID == HWSTUB_TARGET_RK27)
m_soc = "rk27x";
else if(target.dID == HWSTUB_TARGET_PP)
{
struct hwstub_pp_desc_t pp = m_dev->GetPPInfo();
if(pp.wChipID == 0x6110 )
m_soc = "pp6110";
else
m_soc = QString("pp%1").arg(pp.wChipID, 4, 16, QChar('0'));
}
else
m_soc = target.bName;
}
QString HWStubIoBackend::GetSocName()
{
return m_soc;
}
HWStubIoBackend::~HWStubIoBackend()
{
delete m_dev;
}
bool HWStubIoBackend::ReadRegister(soc_addr_t addr, soc_word_t& value)
{
return m_dev->ReadMem(addr, sizeof(value), &value);
}
bool HWStubIoBackend::WriteRegister(soc_addr_t addr, soc_word_t value, WriteMode mode)
{
switch(mode)
{
case Set: addr += 4; break;
case Clear: addr += 8; break;
case Toggle: addr += 12; break;
default: break;
}
return m_dev->WriteMem(addr, sizeof(value), &value);
}
bool HWStubIoBackend::Reload()
{
return true;
}
/**
* HWStubBackendHelper
*/
HWStubBackendHelper::HWStubBackendHelper()
{
#ifdef LIBUSB_NO_HOTPLUG
m_hotplug = false;
#else
m_hotplug = libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG);
if(m_hotplug)
{
int evt = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT;
m_hotplug = LIBUSB_SUCCESS == libusb_hotplug_register_callback(
NULL, (libusb_hotplug_event)evt, LIBUSB_HOTPLUG_ENUMERATE,
LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
&HWStubBackendHelper::HotPlugCallback, reinterpret_cast< void* >(this),
&m_hotplug_handle);
}
#endif /* LIBUSB_NO_HOTPLUG */
}
HWStubBackendHelper::~HWStubBackendHelper()
{
#ifndef LIBUSB_NO_HOTPLUG
if(m_hotplug)
libusb_hotplug_deregister_callback(NULL, m_hotplug_handle);
#endif /* LIBUSB_NO_HOTPLUG */
}
QList< HWStubDevice* > HWStubBackendHelper::GetDevList()
{
QList< HWStubDevice* > list;
libusb_device **dev_list;
ssize_t cnt = hwstub_get_device_list(NULL, &dev_list);
for(int i = 0; i < cnt; i++)
{
HWStubDevice *dev = new HWStubDevice(dev_list[i]);
/* filter out non-hwstub devices */
if(dev->IsValid())
list.push_back(dev);
else
delete dev;
}
libusb_free_device_list(dev_list, 1);
return list;
}
#ifndef LIBUSB_NO_HOTPLUG
void HWStubBackendHelper::OnHotPlug(bool arrived, struct libusb_device *dev)
{
/* signal it */
emit OnDevListChanged(arrived, dev);
}
int HWStubBackendHelper::HotPlugCallback(struct libusb_context *ctx, struct libusb_device *dev,
libusb_hotplug_event event, void *user_data)
{
Q_UNUSED(ctx);
HWStubBackendHelper *helper = reinterpret_cast< HWStubBackendHelper* >(user_data);
switch(event)
{
case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: helper->OnHotPlug(true, dev); break;
case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: helper->OnHotPlug(false, dev); break;
default: break;
}
return 0;
}
#endif /* LIBUSB_NO_HOTPLUG */
bool HWStubBackendHelper::HasHotPlugSupport()
{
return m_hotplug;
}
namespace
{
class lib_usb_init
{
public:
lib_usb_init()
{
libusb_init(NULL);
}
};
lib_usb_init __lib_usb_init;
}
#endif /* HAVE_HWSTUB */
/**
* BackendHelper
*/
BackendHelper::BackendHelper(IoBackend *io_backend, const SocRef& soc)
:m_io_backend(io_backend), m_soc(soc)
{
}
bool BackendHelper::ReadRegister(const QString& dev, const QString& reg, soc_word_t& v)
{
if(m_io_backend->SupportAccess(IoBackend::ByName))
return m_io_backend->ReadRegister("HW." + dev + "." + reg, v);
if(m_io_backend->SupportAccess(IoBackend::ByAddress))
{
soc_addr_t addr;
if(GetRegisterAddress(dev, reg, addr))
return m_io_backend->ReadRegister(addr, v);
}
return false;
}
bool BackendHelper::WriteRegister(const QString& dev, const QString& reg,
soc_word_t v, IoBackend::WriteMode mode)
{
if(m_io_backend->SupportAccess(IoBackend::ByName))
return m_io_backend->WriteRegister("HW." + dev + "." + reg, v, mode);
if(m_io_backend->SupportAccess(IoBackend::ByAddress))
{
soc_addr_t addr;
if(GetRegisterAddress(dev, reg, addr))
return m_io_backend->WriteRegister(addr, v, mode);
}
return false;
}
bool BackendHelper::GetDevRef(const QString& sdev, SocDevRef& ref)
{
for(size_t i = 0; i < m_soc.GetSoc().dev.size(); i++)
{
const soc_dev_t& dev = m_soc.GetSoc().dev[i];
for(size_t j = 0; j < dev.addr.size(); j++)
if(dev.addr[j].name.c_str() == sdev)
{
ref = SocDevRef(m_soc, i, j);
return true;
}
}
return false;
}
bool BackendHelper::GetRegRef(const SocDevRef& dev, const QString& sreg, SocRegRef& ref)
{
const soc_dev_t& sdev = dev.GetDev();
for(size_t i = 0; i < sdev.reg.size(); i++)
{
const soc_reg_t& reg = sdev.reg[i];
for(size_t j = 0; j < reg.addr.size(); j++)
{
if(reg.addr[j].name.c_str() == sreg)
{
ref = SocRegRef(dev, i, j);
return true;
}
}
}
return false;
}
bool BackendHelper::GetFieldRef(const SocRegRef& reg, const QString& sfield, SocFieldRef& ref)
{
for(size_t i = 0; i < reg.GetReg().field.size(); i++)
if(reg.GetReg().field[i].name.c_str() == sfield)
{
ref = SocFieldRef(reg, i);
return true;
}
return false;
}
bool BackendHelper::GetRegisterAddress(const QString& dev, const QString& reg,
soc_addr_t& addr)
{
SocDevRef dev_ref;
SocRegRef reg_ref;
if(!GetDevRef(dev, dev_ref) || !GetRegRef(dev_ref, reg, reg_ref))
return false;
addr = dev_ref.GetDevAddr().addr + reg_ref.GetRegAddr().addr;
return true;
}
bool BackendHelper::ReadRegisterField(const QString& dev, const QString& reg,
const QString& field, soc_word_t& v)
{
SocDevRef dev_ref;
SocRegRef reg_ref;
SocFieldRef field_ref;
if(!GetDevRef(dev, dev_ref) || !GetRegRef(dev_ref, reg, reg_ref) ||
!GetFieldRef(reg_ref, field, field_ref))
return false;
if(!ReadRegister(dev, reg, v))
return false;
v = (v & field_ref.GetField().bitmask()) >> field_ref.GetField().first_bit;
return true;
}
bool BackendHelper::DumpAllRegisters(const QString& filename, bool ignore_errors)
{
FileIoBackend b(filename, QString::fromStdString(m_soc.GetSoc().name));
bool ret = DumpAllRegisters(&b, ignore_errors);
return ret && b.Commit();
}
bool BackendHelper::DumpAllRegisters(IoBackend *backend, bool ignore_errors)
{
BackendHelper bh(backend, m_soc);
bool ret = true;
for(size_t i = 0; i < m_soc.GetSoc().dev.size(); i++)
{
const soc_dev_t& dev = m_soc.GetSoc().dev[i];
for(size_t j = 0; j < dev.addr.size(); j++)
{
QString devname = QString::fromStdString(dev.addr[j].name);
for(size_t k = 0; k < dev.reg.size(); k++)
{
const soc_reg_t& reg = dev.reg[k];
for(size_t l = 0; l < reg.addr.size(); l++)
{
QString regname = QString::fromStdString(reg.addr[l].name);
soc_word_t val;
if(!ReadRegister(devname, regname, val))
{
ret = false;
if(!ignore_errors)
return false;
}
else if(!bh.WriteRegister(devname, regname, val))
{
ret = false;
if(!ignore_errors)
return false;
}
}
}
}
}
return ret;
}