| /*************************************************************************** |
| * __________ __ ___. |
| * 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 = soc_desc_parse_xml(filename.toStdString(), m_soc); |
| soc_desc_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; |
| } |