// // Copyright 2010-2011,2017 Ettus Research, A National Instruments Company // // SPDX-License-Identifier: GPL-3.0-or-later // #include "dboard_ctor_args.hpp" #include #include #include #include #include #include #include #include #include using namespace uhd; using namespace uhd::usrp; /*********************************************************************** * dboard key class to use for look-up **********************************************************************/ class dboard_key_t { public: dboard_key_t(const dboard_id_t& id = dboard_id_t::none(), bool restricted = false) : _rx_id(id), _tx_id(id), _xcvr(false), _restricted(restricted) { } dboard_key_t( const dboard_id_t& rx_id, const dboard_id_t& tx_id, bool restricted = false) : _rx_id(rx_id), _tx_id(tx_id), _xcvr(true), _restricted(restricted) { } dboard_id_t xx_id(void) const { UHD_ASSERT_THROW(not this->is_xcvr()); return this->_rx_id; } dboard_id_t rx_id(void) const { UHD_ASSERT_THROW(this->is_xcvr()); return this->_rx_id; } dboard_id_t tx_id(void) const { UHD_ASSERT_THROW(this->is_xcvr()); return this->_tx_id; } bool is_xcvr(void) const { return this->_xcvr; } bool is_restricted(void) const { return this->_restricted; } private: dboard_id_t _rx_id, _tx_id; bool _xcvr; bool _restricted; }; bool operator==(const dboard_key_t& lhs, const dboard_key_t& rhs) { if (lhs.is_xcvr() and rhs.is_xcvr()) return lhs.rx_id() == rhs.rx_id() and lhs.tx_id() == rhs.tx_id(); if (not lhs.is_xcvr() and not rhs.is_xcvr()) return lhs.xx_id() == rhs.xx_id(); return false; } /*********************************************************************** * storage and registering for dboards **********************************************************************/ // dboard registry tuple: dboard constructor, canonical name, subdev names, container // constructor typedef std::tuple, dboard_manager::dboard_ctor_t> args_t; // map a dboard id to a dboard constructor typedef uhd::dict id_to_args_map_t; UHD_SINGLETON_FCN(id_to_args_map_t, get_id_to_args_map) static void register_dboard_key(const dboard_key_t& dboard_key, dboard_manager::dboard_ctor_t db_subdev_ctor, const std::string& name, const std::vector& subdev_names, dboard_manager::dboard_ctor_t db_container_ctor) { // UHD_LOGGER_TRACE("DBMGR") << "registering: " << name; if (get_id_to_args_map().has_key(dboard_key)) { if (dboard_key.is_xcvr()) throw uhd::key_error(str( boost::format("The dboard id pair [%s, %s] is already registered to %s.") % dboard_key.rx_id().to_string() % dboard_key.tx_id().to_string() % std::get<1>(get_id_to_args_map()[dboard_key]))); else throw uhd::key_error( str(boost::format("The dboard id %s is already registered to %s.") % dboard_key.xx_id().to_string() % std::get<1>(get_id_to_args_map()[dboard_key]))); } get_id_to_args_map()[dboard_key] = args_t(db_subdev_ctor, name, subdev_names, db_container_ctor); } void dboard_manager::register_dboard(const dboard_id_t& dboard_id, dboard_ctor_t db_subdev_ctor, const std::string& name, const std::vector& subdev_names, dboard_ctor_t db_container_ctor) { register_dboard_key( dboard_key_t(dboard_id), db_subdev_ctor, name, subdev_names, db_container_ctor); } void dboard_manager::register_dboard(const dboard_id_t& rx_dboard_id, const dboard_id_t& tx_dboard_id, dboard_ctor_t db_subdev_ctor, const std::string& name, const std::vector& subdev_names, dboard_ctor_t db_container_ctor) { register_dboard_key(dboard_key_t(rx_dboard_id, tx_dboard_id), db_subdev_ctor, name, subdev_names, db_container_ctor); } void dboard_manager::register_dboard_restricted(const dboard_id_t& dboard_id, dboard_ctor_t db_subdev_ctor, const std::string& name, const std::vector& subdev_names, dboard_ctor_t db_container_ctor) { register_dboard_key(dboard_key_t(dboard_id, true), db_subdev_ctor, name, subdev_names, db_container_ctor); } void dboard_manager::register_dboard_restricted(const dboard_id_t& rx_dboard_id, const dboard_id_t& tx_dboard_id, dboard_ctor_t db_subdev_ctor, const std::string& name, const std::vector& subdev_names, dboard_ctor_t db_container_ctor) { register_dboard_key(dboard_key_t(rx_dboard_id, tx_dboard_id, true), db_subdev_ctor, name, subdev_names, db_container_ctor); } std::string dboard_id_t::to_cname(void) const { std::string cname; for (const dboard_key_t& key : get_id_to_args_map().keys()) { if ((not key.is_xcvr() and *this == key.xx_id()) or (key.is_xcvr() and (*this == key.rx_id() or *this == key.tx_id()))) { if (not cname.empty()) cname += ", "; cname += std::get<1>(get_id_to_args_map()[key]); } } return (cname.empty()) ? "Unknown" : cname; } std::string dboard_id_t::to_pp_string(void) const { return str(boost::format("%s (%s)") % this->to_cname() % this->to_string()); } /*********************************************************************** * dboard manager implementation class **********************************************************************/ class dboard_manager_impl : public dboard_manager { public: dboard_manager_impl(dboard_eeprom_t rx_eeprom, dboard_eeprom_t tx_eeprom, dboard_iface::sptr iface, property_tree::sptr subtree, bool defer_db_init); virtual ~dboard_manager_impl(void); inline const std::vector& get_rx_frontends() const { return _rx_frontends; } inline const std::vector& get_tx_frontends() const { return _tx_frontends; } void initialize_dboards(); private: void init(dboard_eeprom_t, dboard_eeprom_t, property_tree::sptr, bool); // list of rx and tx dboards in this dboard_manager // each dboard here is actually a subdevice proxy // the subdevice proxy is internal to the cpp file uhd::dict _rx_dboards; uhd::dict _tx_dboards; std::vector _rx_containers; std::vector _tx_containers; std::vector _rx_frontends; std::vector _tx_frontends; dboard_iface::sptr _iface; void set_nice_dboard_if(void); }; /*********************************************************************** * make routine for dboard manager **********************************************************************/ dboard_manager::sptr dboard_manager::make(dboard_id_t rx_dboard_id, dboard_id_t tx_dboard_id, dboard_id_t gdboard_id, dboard_iface::sptr iface, property_tree::sptr subtree, bool defer_db_init) { dboard_eeprom_t rx_eeprom; dboard_eeprom_t tx_eeprom; rx_eeprom.id = rx_dboard_id; tx_eeprom.id = (gdboard_id == dboard_id_t::none()) ? tx_dboard_id : gdboard_id; return dboard_manager::sptr( new dboard_manager_impl(rx_eeprom, tx_eeprom, iface, subtree, defer_db_init)); } dboard_manager::sptr dboard_manager::make(dboard_eeprom_t rx_eeprom, dboard_eeprom_t tx_eeprom, dboard_eeprom_t gdb_eeprom, dboard_iface::sptr iface, property_tree::sptr subtree, bool defer_db_init) { return dboard_manager::sptr(new dboard_manager_impl(rx_eeprom, (gdb_eeprom.id == dboard_id_t::none()) ? tx_eeprom : gdb_eeprom, iface, subtree, defer_db_init)); } /*********************************************************************** * implementation class methods **********************************************************************/ dboard_manager_impl::dboard_manager_impl(dboard_eeprom_t rx_eeprom, dboard_eeprom_t tx_eeprom, dboard_iface::sptr iface, property_tree::sptr subtree, bool defer_db_init) : _iface(iface) { try { this->init(rx_eeprom, tx_eeprom, subtree, defer_db_init); } catch (const std::exception& e) { UHD_LOGGER_ERROR("DBMGR") << boost::format( "The daughterboard manager encountered a recoverable error in init.\n" "Loading the \"unknown\" daughterboard implementations to continue.\n" "The daughterboard cannot operate until this error is resolved.\n") << e.what(); // clean up the stuff added by the call above if (subtree->exists("rx_frontends")) subtree->remove("rx_frontends"); if (subtree->exists("tx_frontends")) subtree->remove("tx_frontends"); if (subtree->exists("iface")) subtree->remove("iface"); dboard_eeprom_t dummy_eeprom; dummy_eeprom.id = dboard_id_t::none(); this->init(dummy_eeprom, dummy_eeprom, subtree, false); } } void dboard_manager_impl::init(dboard_eeprom_t rx_eeprom, dboard_eeprom_t tx_eeprom, property_tree::sptr subtree, bool defer_db_init) { // find the dboard key matches for the dboard ids dboard_key_t rx_dboard_key, tx_dboard_key, xcvr_dboard_key; for (const dboard_key_t& key : get_id_to_args_map().keys()) { if (key.is_xcvr()) { if (rx_eeprom.id == key.rx_id() and tx_eeprom.id == key.tx_id()) xcvr_dboard_key = key; if (rx_eeprom.id == key.rx_id()) rx_dboard_key = key; // kept to handle warning if (rx_eeprom.id == key.tx_id()) tx_dboard_key = key; // kept to handle warning } else { if (rx_eeprom.id == key.xx_id()) rx_dboard_key = key; if (tx_eeprom.id == key.xx_id()) tx_dboard_key = key; } } // warn for invalid dboard id xcvr combinations if (not xcvr_dboard_key.is_xcvr() and (rx_dboard_key.is_xcvr() or tx_dboard_key.is_xcvr())) { UHD_LOGGER_WARNING("DBMGR") << boost::format("Unknown transceiver board ID combination.\n" "Is your daughter-board mounted properly?\n" "RX dboard ID: %s\n" "TX dboard ID: %s\n") % rx_eeprom.id.to_pp_string() % tx_eeprom.id.to_pp_string(); } // initialize the gpio pins before creating subdevs set_nice_dboard_if(); // conditionally register the dboard iface in the tree if (not(rx_dboard_key.is_restricted() or tx_dboard_key.is_restricted() or xcvr_dboard_key.is_restricted())) { subtree->create("iface").set(_iface); } // dboard constructor args dboard_ctor_args_t db_ctor_args; db_ctor_args.db_iface = _iface; // make xcvr subdevs if (xcvr_dboard_key.is_xcvr()) { // extract data for the xcvr dboard key dboard_ctor_t subdev_ctor; std::string name; std::vector subdevs; dboard_ctor_t container_ctor; std::tie(subdev_ctor, name, subdevs, container_ctor) = get_id_to_args_map()[xcvr_dboard_key]; // create the container class. // a container class exists per N subdevs registered in a register_dboard* call db_ctor_args.sd_name = "common"; db_ctor_args.rx_eeprom = rx_eeprom; db_ctor_args.tx_eeprom = tx_eeprom; db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + db_ctor_args.sd_name); db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + db_ctor_args.sd_name); if (container_ctor) { db_ctor_args.rx_container = container_ctor(&db_ctor_args); } else { db_ctor_args.rx_container = dboard_base::sptr(); } db_ctor_args.tx_container = db_ctor_args.rx_container; // Same TX and RX container // create the xcvr object for each subdevice for (const std::string& subdev : subdevs) { db_ctor_args.sd_name = subdev; db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + db_ctor_args.sd_name); db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + db_ctor_args.sd_name); dboard_base::sptr xcvr_dboard = subdev_ctor(&db_ctor_args); _rx_dboards[subdev] = xcvr_dboard; _tx_dboards[subdev] = xcvr_dboard; xcvr_dboard->initialize(); } // initialize the container after all subdevs have been created if (container_ctor) { if (defer_db_init) { _rx_containers.push_back(db_ctor_args.rx_container); } else { db_ctor_args.rx_container->initialize(); } } // Populate frontend names in-order. // We cannot use _xx_dboards.keys() here because of the ordering requirement _rx_frontends = subdevs; _tx_frontends = subdevs; } // make tx and rx subdevs (separate subdevs for rx and tx dboards) else { // force the rx key to the unknown board for bad combinations if (rx_dboard_key.is_xcvr() or rx_dboard_key.xx_id() == dboard_id_t::none()) { rx_dboard_key = dboard_key_t(0xfff1); } // extract data for the rx dboard key dboard_ctor_t rx_dboard_ctor; std::string rx_name; std::vector rx_subdevs; dboard_ctor_t rx_cont_ctor; std::tie(rx_dboard_ctor, rx_name, rx_subdevs, rx_cont_ctor) = get_id_to_args_map()[rx_dboard_key]; // create the container class. // a container class exists per N subdevs registered in a register_dboard* call db_ctor_args.sd_name = "common"; db_ctor_args.rx_eeprom = rx_eeprom; db_ctor_args.tx_eeprom.id = dboard_id_t::none(); db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + db_ctor_args.sd_name); db_ctor_args.tx_subtree = property_tree::sptr(); if (rx_cont_ctor) { db_ctor_args.rx_container = rx_cont_ctor(&db_ctor_args); } else { db_ctor_args.rx_container = dboard_base::sptr(); } // make the rx subdevs for (const std::string& subdev : rx_subdevs) { db_ctor_args.sd_name = subdev; db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + db_ctor_args.sd_name); _rx_dboards[subdev] = rx_dboard_ctor(&db_ctor_args); _rx_dboards[subdev]->initialize(); } // initialize the container after all subdevs have been created if (rx_cont_ctor) { if (defer_db_init) { _rx_containers.push_back(db_ctor_args.rx_container); } else { db_ctor_args.rx_container->initialize(); } } // force the tx key to the unknown board for bad combinations if (tx_dboard_key.is_xcvr() or tx_dboard_key.xx_id() == dboard_id_t::none()) { tx_dboard_key = dboard_key_t(0xfff0); } // extract data for the tx dboard key dboard_ctor_t tx_dboard_ctor; std::string tx_name; std::vector tx_subdevs; dboard_ctor_t tx_cont_ctor; std::tie(tx_dboard_ctor, tx_name, tx_subdevs, tx_cont_ctor) = get_id_to_args_map()[tx_dboard_key]; // create the container class. // a container class exists per N subdevs registered in a register_dboard* call db_ctor_args.sd_name = "common"; db_ctor_args.rx_eeprom.id = dboard_id_t::none(); db_ctor_args.tx_eeprom = tx_eeprom; db_ctor_args.rx_subtree = property_tree::sptr(); db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + db_ctor_args.sd_name); if (tx_cont_ctor) { db_ctor_args.tx_container = tx_cont_ctor(&db_ctor_args); } else { db_ctor_args.tx_container = dboard_base::sptr(); } // make the tx subdevs for (const std::string& subdev : tx_subdevs) { db_ctor_args.sd_name = subdev; db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + db_ctor_args.sd_name); _tx_dboards[subdev] = tx_dboard_ctor(&db_ctor_args); _tx_dboards[subdev]->initialize(); } // initialize the container after all subdevs have been created if (tx_cont_ctor) { if (defer_db_init) { _tx_containers.push_back(db_ctor_args.tx_container); } else { db_ctor_args.tx_container->initialize(); } } // Populate frontend names in-order. // We cannot use _xx_dboards.keys() here because of the ordering requirement _rx_frontends = rx_subdevs; _tx_frontends = tx_subdevs; } } void dboard_manager_impl::initialize_dboards(void) { for (dboard_base::sptr& _rx_container : _rx_containers) { _rx_container->initialize(); } for (dboard_base::sptr& _tx_container : _tx_containers) { _tx_container->initialize(); } } dboard_manager_impl::~dboard_manager_impl(void) { UHD_SAFE_CALL(set_nice_dboard_if();) } void dboard_manager_impl::set_nice_dboard_if(void) { // make a list of possible unit types const std::vector units{ dboard_iface::UNIT_RX, dboard_iface::UNIT_TX}; // set nice settings on each unit for (dboard_iface::unit_t unit : units) { _iface->set_gpio_ddr(unit, 0x0000); // all inputs _iface->set_gpio_out(unit, 0x0000); // all low _iface->set_pin_ctrl(unit, 0x0000); // all gpio _iface->set_clock_enabled(unit, false); // clock off } }