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