A modern, network-enabled music player platform built on Rockbox technology.
rockboxd.tsiry-sandratraina.com
rust
deno
navidrome
airplay
libadwaita
zig
mpris
snapcast
mpd
rockbox
audio
subsonic
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2014 by Amaury Pouly
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20#include <QFile>
21#include <QTextStream>
22#include <QDebug>
23#include <QFileInfo>
24#include <QFont>
25#include "backend.h"
26
27/**
28 * SocFile
29 */
30SocFile::SocFile()
31 :m_valid(true)
32{
33}
34
35SocFile::SocFile(const QString& filename)
36 :m_filename(filename)
37{
38 soc_desc::error_context_t ctx;
39 m_valid = soc_desc::parse_xml(filename.toStdString(), m_soc, ctx);
40}
41
42bool SocFile::IsValid()
43{
44 return m_valid;
45}
46
47soc_desc::soc_ref_t SocFile::GetSocRef()
48{
49 if(m_valid)
50 return soc_desc::soc_ref_t(&m_soc);
51 else
52 return soc_desc::soc_ref_t();
53}
54
55QString SocFile::GetFilename()
56{
57 return m_filename;
58}
59
60/**
61 * Backend
62 */
63
64Backend::Backend()
65{
66}
67
68
69QList< SocFileRef > Backend::GetSocFileList()
70{
71 QList< SocFileRef > list;
72 for(std::list< SocFile >::iterator it = m_socs.begin(); it != m_socs.end(); ++it)
73 {
74 if(it->IsValid())
75 list.append(SocFileRef(&(*it)));
76 }
77 return list;
78}
79
80QList< soc_desc::soc_ref_t > Backend::GetSocList()
81{
82 QList< soc_desc::soc_ref_t > list;
83 for(std::list< SocFile >::iterator it = m_socs.begin(); it != m_socs.end(); ++it)
84 {
85 soc_desc::soc_ref_t r = it->GetSocRef();
86 if(r.valid())
87 list.append(r);
88 }
89 return list;
90}
91
92bool Backend::LoadSocDesc(const QString& filename)
93{
94 SocFile f(filename);
95 if(!f.IsValid())
96 return false;
97 m_socs.push_back(f);
98 emit OnSocAdded(SocFileRef(&m_socs.back()));
99 return true;
100}
101
102IoBackend *Backend::CreateFileIoBackend(const QString& filename)
103{
104 return new FileIoBackend(filename);
105}
106
107IoBackend *Backend::CreateDummyIoBackend()
108{
109 return new DummyIoBackend();
110}
111
112#ifdef HAVE_HWSTUB
113IoBackend *Backend::CreateHWStubIoBackend(HWStubDevice *dev)
114{
115 return new HWStubIoBackend(dev);
116}
117#endif
118
119/**
120 * DummyIoBackend
121 */
122
123DummyIoBackend::DummyIoBackend()
124{
125}
126
127bool DummyIoBackend::IsValid()
128{
129 return false;
130}
131
132QString DummyIoBackend::GetSocName()
133{
134 return "";
135}
136
137bool DummyIoBackend::ReadRegister(soc_addr_t addr, soc_word_t& value,
138 unsigned width)
139{
140 Q_UNUSED(addr);
141 Q_UNUSED(value);
142 Q_UNUSED(width);
143 return false;
144}
145
146bool DummyIoBackend::Reload()
147{
148 return false;
149}
150
151bool DummyIoBackend::IsReadOnly()
152{
153 return true;
154}
155
156bool DummyIoBackend::WriteRegister(soc_addr_t addr, soc_word_t value,
157 unsigned width, WriteMode mode)
158{
159 Q_UNUSED(addr);
160 Q_UNUSED(value);
161 Q_UNUSED(mode);
162 Q_UNUSED(width);
163 return false;
164}
165
166bool DummyIoBackend::IsDirty()
167{
168 return false;
169}
170
171bool DummyIoBackend::Commit()
172{
173 return false;
174}
175
176/**
177 * RamIoBackend
178 */
179RamIoBackend::RamIoBackend(const QString& soc_name)
180{
181 m_soc = soc_name;
182}
183
184bool RamIoBackend::IsValid()
185{
186 return m_soc != "";
187}
188
189QString RamIoBackend::GetSocName()
190{
191 return m_soc;
192}
193
194void RamIoBackend::SetSocName(const QString& soc_name)
195{
196 m_soc = soc_name;
197}
198
199bool RamIoBackend::RamIoBackend::Reload()
200{
201 return false;
202}
203
204bool RamIoBackend::IsReadOnly()
205{
206 return false;
207}
208
209bool RamIoBackend::ReadRegister(soc_addr_t addr, soc_word_t& value,
210 unsigned width)
211{
212 Q_UNUSED(width);
213 QMap<soc_addr_t, soc_word_t>::const_iterator it = m_map.find(addr);
214 if(it == m_map.end())
215 return false;
216 value = it.value();
217 return true;
218}
219
220void RamIoBackend::DeleteAll()
221{
222 m_map.clear();
223}
224
225bool RamIoBackend::WriteRegister(soc_addr_t addr, soc_word_t value,
226 unsigned width, WriteMode mode)
227{
228 Q_UNUSED(width);
229 switch(mode)
230 {
231 case Write: m_map[addr] = value; return true;
232 case Set: m_map[addr] |= value; return true;
233 case Clear: m_map[addr] &= ~value; return true;
234 case Toggle: m_map[addr] ^= value; return true;
235 default: return false;
236 }
237}
238
239bool RamIoBackend::IsDirty()
240{
241 return false;
242}
243
244bool RamIoBackend::Commit()
245{
246 return false;
247}
248
249/**
250 * FileIoBackend
251 */
252
253FileIoBackend::FileIoBackend(const QString& filename, const QString& soc_name)
254 :RamIoBackend(soc_name)
255{
256 m_filename = filename;
257 m_valid = false;
258 Reload();
259}
260
261bool FileIoBackend::IsValid()
262{
263 return m_valid;
264}
265
266bool FileIoBackend::Reload()
267{
268 m_valid = false;
269 QFile file(m_filename);
270 if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
271 return false;
272 DeleteAll();
273
274 QTextStream in(&file);
275 while(!in.atEnd())
276 {
277 QString line = in.readLine();
278 int idx = line.indexOf('=');
279 if(idx == -1)
280 continue;
281 QString key_str = line.left(idx).trimmed();
282 QString val_str = line.mid(idx + 1).trimmed();
283 bool key_ok,val_ok;
284 soc_word_t val = val_str.toULong(&val_ok, 0);
285 soc_word_t key = key_str.toULong(&key_ok, 0);
286 if(key_str == "soc")
287 m_soc = val_str;
288 else if(key_ok && val_ok)
289 RamIoBackend::WriteRegister(key, val, 32, Write);
290 }
291 m_readonly = !QFileInfo(file).isWritable();
292 m_dirty = false;
293 m_valid = true;
294 return true;
295}
296
297bool FileIoBackend::WriteRegister(soc_addr_t addr, soc_word_t value,
298 unsigned width, WriteMode mode)
299{
300 m_dirty = true;
301 return RamIoBackend::WriteRegister(addr, value, width, mode);
302}
303
304bool FileIoBackend::Commit()
305{
306 if(!m_dirty)
307 return true;
308 QFile file(m_filename);
309 if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
310 return false;
311 QTextStream out(&file);
312 out << "soc = " << m_soc << "\n";
313 QMapIterator< soc_addr_t, soc_word_t > it(m_map);
314 while(it.hasNext())
315 {
316 it.next();
317 out << hex << showbase << it.key() << " = " << hex << showbase << it.value() << "\n";
318 }
319 out.flush();
320 return file.flush();
321}
322
323bool FileIoBackend::IsReadOnly()
324{
325 return m_readonly;
326}
327
328bool FileIoBackend::IsDirty()
329{
330 return m_dirty;
331}
332
333QString FileIoBackend::GetFileName()
334{
335 return m_filename;
336}
337
338#ifdef HAVE_HWSTUB
339/**
340 * HWStubManager
341 */
342HWStubManager *HWStubManager::g_inst = nullptr;
343
344HWStubManager::HWStubManager()
345{
346 Add("Default", QString::fromStdString(hwstub::uri::default_uri().full_uri()));
347}
348
349HWStubManager::~HWStubManager()
350{
351}
352
353HWStubManager *HWStubManager::Get()
354{
355 if(g_inst == nullptr)
356 g_inst = new HWStubManager();
357 return g_inst;
358}
359
360bool HWStubManager::Add(const QString& name, const QString& uri)
361{
362 struct Context ctx;
363 ctx.name = name;
364 ctx.uri = uri;
365 ctx.context = hwstub::uri::create_context(uri.toStdString());
366 if(!ctx.context)
367 return false;
368 ctx.context->start_polling();
369 beginInsertRows(QModelIndex(), m_list.size(), m_list.size());
370 m_list.push_back(ctx);
371 endInsertRows();
372 return true;
373}
374
375void HWStubManager::Clear()
376{
377 m_list.clear();
378}
379
380int HWStubManager::rowCount(const QModelIndex& parent) const
381{
382 Q_UNUSED(parent);
383 return m_list.size();
384}
385
386int HWStubManager::columnCount(const QModelIndex& parent) const
387{
388 Q_UNUSED(parent);
389 return 2;
390}
391
392std::shared_ptr< hwstub::context > HWStubManager::GetContext(int row)
393{
394 if(row < 0 || (size_t)row >= m_list.size())
395 return std::shared_ptr< hwstub::context >();
396 else
397 return m_list[row].context;
398}
399
400QVariant HWStubManager::data(const QModelIndex& index, int role) const
401{
402 if(index.row() < 0 || (size_t)index.row() >= m_list.size())
403 return QVariant();
404 int section = index.column();
405 const Context& ctx = m_list[index.row()];
406 if(section == GetNameColumn())
407 {
408 if(role == Qt::DisplayRole || role == Qt::EditRole)
409 return QVariant(ctx.name);
410 }
411 else if(section == GetUriColumn())
412 {
413 if(role == Qt::DisplayRole)
414 return QVariant(ctx.uri);
415 }
416 return QVariant();
417}
418
419QVariant HWStubManager::headerData(int section, Qt::Orientation orientation, int role) const
420{
421 if(orientation == Qt::Vertical)
422 return QVariant();
423 if(role != Qt::DisplayRole)
424 return QVariant();
425 if(section == GetNameColumn())
426 return QVariant("Name");
427 else if(section == GetUriColumn())
428 return QVariant("URI");
429 return QVariant();
430}
431
432Qt::ItemFlags HWStubManager::flags(const QModelIndex& index) const
433{
434 Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
435 int section = index.column();
436 if(section == GetNameColumn())
437 flags |= Qt::ItemIsEditable;
438 return flags;
439}
440
441bool HWStubManager::setData(const QModelIndex& index, const QVariant& value, int role)
442{
443 if(role != Qt::EditRole)
444 return false;
445 if(index.row() < 0 || (size_t)index.row() >= m_list.size())
446 return false;
447 if(index.column() != GetNameColumn())
448 return false;
449 m_list[index.row()].name = value.toString();
450 emit dataChanged(index, index);
451 return true;
452}
453
454int HWStubManager::GetNameColumn() const
455{
456 return 0;
457}
458
459int HWStubManager::GetUriColumn() const
460{
461 return 1;
462}
463
464QString HWStubManager::GetFriendlyName(std::shared_ptr< hwstub::device > device)
465{
466 /* try to open the device */
467 std::shared_ptr< hwstub::handle > handle;
468 hwstub::error err = device->open(handle);
469 if(err != hwstub::error::SUCCESS)
470 goto Lfallback;
471 /* get target descriptor */
472 struct hwstub_target_desc_t target_desc;
473 err = handle->get_target_desc(target_desc);
474 if(err != hwstub::error::SUCCESS)
475 goto Lfallback;
476 return QString::fromStdString(target_desc.bName);
477
478 /* fallback: don't open the device */
479Lfallback:
480 hwstub::usb::device *udev = dynamic_cast< hwstub::usb::device* >(device.get());
481 if(udev)
482 {
483 return QString("USB Bus %1 Device %2: ID %3:%4")
484 .arg(udev->get_bus_number()).arg(udev->get_address(), 3, 10, QChar('0'))
485 .arg(udev->get_vid(), 4, 16, QChar('0')).arg(udev->get_pid(), 4, 16, QChar('0'));
486 }
487 else
488 return QString("<Unknown device>");
489}
490
491/**
492 * HWStubContextModel
493 */
494HWStubContextModel::HWStubContextModel(QObject *parent)
495 :QAbstractTableModel(parent), m_has_dummy(false)
496{
497}
498
499HWStubContextModel::~HWStubContextModel()
500{
501 SetContext(std::shared_ptr< hwstub::context >());
502}
503
504void HWStubContextModel::SetContext(std::shared_ptr< hwstub::context > context)
505{
506 int first_row = m_has_dummy ? 1: 0;
507 /* clear previous model if any */
508 if(m_list.size() > 0)
509 {
510 beginRemoveRows(QModelIndex(), first_row, first_row + m_list.size() - 1);
511 m_list.clear();
512 endRemoveRows();
513 }
514 /* don't forget to unregister callback if context still exists */
515 std::shared_ptr< hwstub::context > ctx = m_context.lock();
516 if(ctx)
517 ctx->unregister_callback(m_callback_ref);
518 /* get new context */
519 m_context = context;
520 if(context)
521 {
522 /* register new callback */
523 m_callback_ref = context->register_callback(
524 std::bind(&HWStubContextModel::OnDevChangeLow, this, std::placeholders::_1,
525 std::placeholders::_2, std::placeholders::_3));
526 /* get dev list */
527 std::vector< std::shared_ptr< hwstub::device > > list;
528 hwstub::error err = context->get_device_list(list);
529 if(err == hwstub::error::SUCCESS)
530 {
531 beginInsertRows(QModelIndex(), first_row, first_row + list.size() - 1);
532 for(auto& d : list)
533 {
534 Device dev;
535 dev.name = GetFriendlyName(d);
536 dev.device = d;
537 m_list.push_back(dev);
538 }
539 endInsertRows();
540 }
541 }
542}
543
544void HWStubContextModel::EnableDummy(bool en, const QString& text)
545{
546 /* if needed, create/remove raw */
547 if(m_has_dummy && !en)
548 {
549 /* remove row */
550 beginRemoveRows(QModelIndex(), 0, 0);
551 m_has_dummy = false;
552 endRemoveRows();
553 }
554 else if(!m_has_dummy && en)
555 {
556 /* add row */
557 beginInsertRows(QModelIndex(), 0, 0);
558 m_has_dummy = true;
559 m_dummy_text = text;
560 endInsertRows();
561 }
562 else if(en)
563 {
564 /* text change only */
565 emit dataChanged(index(0, GetNameColumn()), index(0, GetNameColumn()));
566 }
567}
568
569int HWStubContextModel::rowCount(const QModelIndex& parent) const
570{
571 Q_UNUSED(parent);
572 return m_list.size() + (m_has_dummy ? 1 : 0);
573}
574
575int HWStubContextModel::columnCount(const QModelIndex& parent) const
576{
577 Q_UNUSED(parent);
578 return 1;
579}
580
581QVariant HWStubContextModel::data(const QModelIndex& index, int role) const
582{
583 int first_row = m_has_dummy ? 1: 0;
584 /* special case for dummy */
585 if(m_has_dummy && index.row() == 0)
586 {
587 int section = index.column();
588 if(section == GetNameColumn())
589 {
590 if(role == Qt::DisplayRole)
591 return QVariant(m_dummy_text);
592 else if(role == Qt::FontRole)
593 {
594 QFont font;
595 font.setItalic(true);
596 return QVariant(font);
597 }
598 }
599 return QVariant();
600 }
601
602 if(index.row() < first_row || (size_t)index.row() >= first_row + m_list.size())
603 return QVariant();
604 int section = index.column();
605 if(section == GetNameColumn())
606 {
607 if(role == Qt::DisplayRole)
608 return QVariant(m_list[index.row() - first_row].name);
609 }
610 return QVariant();
611}
612
613QVariant HWStubContextModel::headerData(int section, Qt::Orientation orientation, int role) const
614{
615 if(orientation == Qt::Vertical)
616 return QVariant();
617 if(role != Qt::DisplayRole)
618 return QVariant();
619 if(section == GetNameColumn())
620 return QVariant("Friendly name");
621 return QVariant();
622}
623
624Qt::ItemFlags HWStubContextModel::flags(const QModelIndex& index) const
625{
626 Q_UNUSED(index);
627 return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
628}
629
630int HWStubContextModel::GetNameColumn() const
631{
632 return 0;
633}
634
635std::shared_ptr< hwstub::device > HWStubContextModel::GetDevice(int row)
636{
637 int first_row = m_has_dummy ? 1: 0;
638 /* special case for dummy */
639 if(row < first_row || (size_t)row >= first_row + m_list.size())
640 return std::shared_ptr< hwstub::device >();
641 else
642 return m_list[row - first_row].device;
643}
644
645QString HWStubContextModel::GetFriendlyName(std::shared_ptr< hwstub::device > device)
646{
647 return HWStubManager::GetFriendlyName(device);
648}
649
650namespace
651{
652 struct dev_change_t
653 {
654 std::shared_ptr< hwstub::context > ctx;
655 bool arrived;
656 std::shared_ptr< hwstub::device > device;
657 };
658}
659
660void HWStubContextModel::OnDevChangeLow(std::shared_ptr< hwstub::context > ctx,
661 bool arrived, std::shared_ptr< hwstub::device > device)
662{
663 /* calling Qt function from non-Qt thread is unsafe. Since the polling thread
664 * is a pthread, the safest way to use Qt invoke mecanism to make it run
665 * on the event loop */
666 dev_change_t *evt = new dev_change_t;
667 evt->ctx = ctx;
668 evt->arrived = arrived;
669 evt->device = device;
670 QMetaObject::invokeMethod(this, "OnDevChangeUnsafe", Q_ARG(void *, (void *)evt));
671}
672
673void HWStubContextModel::OnDevChangeUnsafe(void *data)
674{
675 dev_change_t *evt = (dev_change_t *)data;
676 OnDevChange(evt->ctx, evt->arrived, evt->device);
677 delete evt;
678}
679
680void HWStubContextModel::OnDevChange(std::shared_ptr< hwstub::context > ctx, bool arrived,
681 std::shared_ptr< hwstub::device > device)
682{
683 int first_row = m_has_dummy ? 1: 0;
684 Q_UNUSED(ctx);
685 if(arrived)
686 {
687 Device dev;
688 dev.name = GetFriendlyName(device);
689 dev.device = device;
690 beginInsertRows(QModelIndex(), first_row + m_list.size(),
691 first_row + m_list.size());
692 m_list.push_back(dev);
693 endInsertRows();
694 }
695 else
696 {
697 /* find device in the list */
698 auto it = m_list.begin();
699 int idx = 0;
700 for(; it != m_list.end(); ++it, ++idx)
701 if(it->device == device)
702 break;
703 if(it == m_list.end())
704 return;
705 /* remove it */
706 beginRemoveRows(QModelIndex(), first_row + idx, first_row + idx);
707 m_list.erase(it);
708 endRemoveRows();
709 }
710}
711
712/**
713 * HWStubDevice
714 */
715HWStubDevice::HWStubDevice(std::shared_ptr< hwstub::device > device)
716{
717 m_valid = Probe(device);
718}
719
720HWStubDevice::~HWStubDevice()
721{
722}
723
724bool HWStubDevice::Probe(std::shared_ptr<hwstub::device> device)
725{
726 if(!device)
727 return false;
728 hwstub::error err = device->open(m_handle);
729 if(err != hwstub::error::SUCCESS)
730 return false;
731 // get target information
732 err = m_handle->get_target_desc(m_hwdev_target);
733 if(err != hwstub::error::SUCCESS)
734 return false;
735 // get STMP/PP information
736 if(m_hwdev_target.dID == HWSTUB_TARGET_STMP)
737 {
738 err = m_handle->get_stmp_desc(m_hwdev_stmp);
739 if(err != hwstub::error::SUCCESS)
740 return false;
741 }
742 else if(m_hwdev_target.dID == HWSTUB_TARGET_PP)
743 {
744 err = m_handle->get_pp_desc(m_hwdev_pp);
745 if(err != hwstub::error::SUCCESS)
746 return false;
747 }
748 else if(m_hwdev_target.dID == HWSTUB_TARGET_JZ)
749 {
750 err = m_handle->get_jz_desc(m_hwdev_jz);
751 if(err != hwstub::error::SUCCESS)
752 return false;
753 }
754 m_name = HWStubManager::GetFriendlyName(device);
755 return true;
756}
757
758bool HWStubDevice::ReadMem(soc_addr_t addr, size_t length, void *buffer)
759{
760 size_t len = length;
761 hwstub::error err = m_handle->read(addr, buffer, len, true);
762 return err == hwstub::error::SUCCESS && len == length;
763}
764
765bool HWStubDevice::WriteMem(soc_addr_t addr, size_t length, void *buffer)
766{
767 size_t len = length;
768 hwstub::error err = m_handle->write(addr, buffer, len, true);
769 return err == hwstub::error::SUCCESS && len == length;
770}
771
772bool HWStubDevice::IsValid()
773{
774 return m_valid;
775}
776
777QString HWStubDevice::GetFriendlyName()
778{
779 return m_name;
780}
781
782/**
783 * HWStubIoBackend
784 */
785
786HWStubIoBackend::HWStubIoBackend(HWStubDevice *dev)
787{
788 m_dev = dev;
789
790 struct hwstub_target_desc_t target = m_dev->GetTargetInfo();
791 if(target.dID == HWSTUB_TARGET_STMP)
792 {
793 struct hwstub_stmp_desc_t stmp = m_dev->GetSTMPInfo();
794 if(stmp.wChipID == 0x3780)
795 m_soc = "imx233";
796 else if(stmp.wChipID >= 0x3700 && stmp.wChipID < 0x3780)
797 m_soc = "stmp3700";
798 else if(stmp.wChipID >= 0x3600 && stmp.wChipID < 0x3700)
799 m_soc = "stmp3600";
800 else
801 m_soc = QString("stmp%1").arg(stmp.wChipID, 4, 16, QChar('0'));
802 }
803 else if(target.dID == HWSTUB_TARGET_JZ)
804 {
805 struct hwstub_jz_desc_t jz = m_dev->GetJZInfo();
806 m_soc = QString("jz%1").arg(jz.wChipID, 4, 16, QChar('0'));
807 if(jz.bRevision != 0)
808 m_soc.append(QChar(jz.bRevision).toLower());
809 }
810 else if(target.dID == HWSTUB_TARGET_RK27)
811 m_soc = "rk27x";
812 else if(target.dID == HWSTUB_TARGET_PP)
813 {
814 struct hwstub_pp_desc_t pp = m_dev->GetPPInfo();
815 if(pp.wChipID == 0x6110 )
816 m_soc = "pp6110";
817 else
818 m_soc = QString("pp%1").arg(pp.wChipID, 4, 16, QChar('0'));
819 }
820 else if(target.dID == HWSTUB_TARGET_ATJ)
821 m_soc = "atj213x";
822 else
823 m_soc = target.bName;
824}
825
826QString HWStubIoBackend::GetSocName()
827{
828 return m_soc;
829}
830
831HWStubIoBackend::~HWStubIoBackend()
832{
833 delete m_dev;
834}
835
836bool HWStubIoBackend::IsValid()
837{
838 return m_dev->IsValid();
839}
840
841bool HWStubIoBackend::IsReadOnly()
842{
843 return false;
844}
845
846bool HWStubIoBackend::IsDirty()
847{
848 return false;
849}
850
851bool HWStubIoBackend::Commit()
852{
853 return true;
854}
855
856HWStubDevice *HWStubIoBackend::GetDevice()
857{
858 return m_dev;
859}
860
861bool HWStubIoBackend::ReadRegister(soc_addr_t addr, soc_word_t& value,
862 unsigned width)
863{
864 if(width != 8 && width != 16 && width != 32)
865 return false;
866 return m_dev->ReadMem(addr, width / 8, &value);
867}
868
869bool HWStubIoBackend::WriteRegister(soc_addr_t addr, soc_word_t value,
870 unsigned width, WriteMode mode)
871{
872 if(width != 8 && width != 16 && width != 32)
873 return false;
874 switch(mode)
875 {
876 case Set: addr += 4; break;
877 case Clear: addr += 8; break;
878 case Toggle: addr += 12; break;
879 default: break;
880 }
881 return m_dev->WriteMem(addr, width / 8, &value);
882}
883
884bool HWStubIoBackend::Reload()
885{
886 return true;
887}
888
889#endif /* HAVE_HWSTUB */
890
891/**
892 * BackendHelper
893 */
894
895BackendHelper::BackendHelper(IoBackend *io_backend, const soc_desc::soc_ref_t& soc)
896 :m_io_backend(io_backend), m_soc(soc)
897{
898}
899
900QString BackendHelper::GetPath(const soc_desc::node_inst_t& inst)
901{
902 if(!inst.valid() || inst.is_root())
903 return QString();
904 QString s = GetPath(inst.parent());
905 if(!s.isEmpty())
906 s += ".";
907 s += inst.name().c_str();
908 if(inst.is_indexed())
909 s = QString("%1[%2]").arg(s).arg(inst.index());
910 return s;
911}
912
913soc_desc::node_inst_t BackendHelper::ParsePath(const QString& path)
914{
915 soc_desc::node_inst_t inst = m_soc.root_inst();
916 /* empty path is root */
917 if(path.isEmpty())
918 return inst;
919 int pos = 0;
920 while(pos < path.size())
921 {
922 /* try to find the next separator */
923 int next = path.indexOf('.', pos);
924 if(next == -1)
925 next = path.size();
926 /* try to find the index, if any */
927 int lidx = path.indexOf('[', pos);
928 if(lidx == -1 || lidx > next)
929 lidx = next;
930 /* extract name */
931 std::string name = path.mid(pos, lidx - pos).toStdString();
932 /* and index */
933 if(lidx < next)
934 {
935 int ridx = path.indexOf(']', lidx + 1);
936 /* syntax error ? */
937 if(ridx == -1 || ridx > next)
938 return soc_desc::node_inst_t();
939 /* invalid number ? */
940 bool ok = false;
941 size_t idx = path.mid(lidx + 1, ridx - lidx - 1).toUInt(&ok);
942 if(ok)
943 inst = inst.child(name, idx);
944 else
945 inst = soc_desc::node_inst_t();
946 }
947 else
948 inst = inst.child(name);
949 /* advance right after the separator */
950 pos = next + 1;
951 }
952 return inst;
953}
954
955bool BackendHelper::ReadRegister(const soc_desc::node_inst_t& inst,
956 soc_word_t& v)
957{
958 soc_addr_t addr;
959 if(!GetRegisterAddress(inst, addr))
960 return false;
961 return m_io_backend->ReadRegister(addr, v, inst.node().reg().get()->width);
962}
963
964bool BackendHelper::WriteRegister(const soc_desc::node_inst_t& inst,
965 soc_word_t v, IoBackend::WriteMode mode)
966{
967 soc_addr_t addr;
968 if(!GetRegisterAddress(inst, addr))
969 return false;
970 return m_io_backend->WriteRegister(addr, v, inst.node().reg().get()->width, mode);
971}
972
973bool BackendHelper::GetRegisterAddress(const soc_desc::node_inst_t& inst,
974 soc_addr_t& addr)
975{
976 if(!inst.valid())
977 return false;
978 addr = inst.addr();
979 return true;
980}
981
982bool BackendHelper::ReadRegisterField(const soc_desc::node_inst_t& inst,
983 const QString& field, soc_word_t& v)
984{
985 soc_desc::field_ref_t ref = inst.node().reg().field(field.toStdString());
986 if(!ref.valid())
987 return false;
988 if(!ReadRegister(inst, v))
989 return false;
990 v = (v & ref.get()->bitmask()) >> ref.get()->pos;
991 return true;
992}
993
994bool BackendHelper::DumpAllRegisters(const QString& filename, bool ignore_errors)
995{
996 FileIoBackend b(filename, QString::fromStdString(m_soc.get()->name));
997 bool ret = DumpAllRegisters(&b, ignore_errors);
998 return ret && b.Commit();
999}
1000
1001bool BackendHelper::DumpAllRegisters(IoBackend *backend, bool ignore_errors)
1002{
1003 BackendHelper helper(backend, m_soc);
1004 return DumpAllRegisters(&helper, m_soc.root_inst(), ignore_errors);
1005}
1006
1007bool BackendHelper::DumpAllRegisters(BackendHelper *bh,
1008 const soc_desc::node_inst_t& inst, bool ignore_errors)
1009{
1010 bool ret = true;
1011 if(inst.node().reg().valid())
1012 {
1013 soc_word_t val;
1014 if(!ReadRegister(inst, val))
1015 {
1016 ret = false;
1017 if(!ignore_errors)
1018 return false;
1019 }
1020 else if(!bh->WriteRegister(inst, val))
1021 {
1022 ret = false;
1023 if(!ignore_errors)
1024 return false;
1025 }
1026 }
1027 std::vector< soc_desc::node_inst_t > list = inst.children();
1028 for(size_t i = 0; i < list.size(); i++)
1029 {
1030 if(!DumpAllRegisters(bh, list[i], ignore_errors))
1031 {
1032 ret = false;
1033 if(!ignore_errors)
1034 return false;
1035 }
1036 }
1037 return ret;
1038}