1 //
2 // Copyright 2018 Ettus Research, a National Instruments Company
3 // Copyright 2019 Ettus Research, a National Instruments Brand
4 //
5 // SPDX-License-Identifier: GPL-3.0-or-later
6 //
7 
8 // property tree initialization code
9 
10 #include "mpmd_impl.hpp"
11 #include <uhd/property_tree.hpp>
12 #include <uhd/types/component_file.hpp>
13 #include <uhd/types/eeprom.hpp>
14 #include <uhd/types/sensors.hpp>
15 #include <uhd/usrp/mboard_eeprom.hpp>
16 
17 using namespace uhd;
18 using namespace uhd::mpmd;
19 
20 namespace {
21 
22 /*! Update a component using all required files. For example, when updating the FPGA image
23  * (.bit or .bin), users can provide a new overlay image (DTS) to apply in addition.
24  *
25  * \param comps Vector of component files to be updated
26  * \param mb Reference to the actual device
27  */
_update_component(const uhd::usrp::component_files_t & comps,mpmd_mboard_impl * mb)28 uhd::usrp::component_files_t _update_component(
29     const uhd::usrp::component_files_t& comps, mpmd_mboard_impl* mb)
30 {
31     // Construct the arguments to update component
32     std::vector<std::vector<uint8_t>> all_data;
33     std::vector<std::map<std::string, std::string>> all_metadata;
34     // Also construct a copy of just the metadata to store in the property tree
35     uhd::usrp::component_files_t all_comps_copy;
36 
37     for (const auto& comp : comps) {
38         // Make a map for update components args
39         std::map<std::string, std::string> metadata;
40         // Make a component copy to add to the property tree
41         uhd::usrp::component_file_t comp_copy;
42         // Copy the metadata
43         for (const auto& key : comp.metadata.keys()) {
44             metadata[key]           = comp.metadata[key];
45             comp_copy.metadata[key] = comp.metadata[key];
46         }
47         // Copy to the update component args
48         all_data.push_back(comp.data);
49         all_metadata.push_back(metadata);
50         // Copy to the property tree
51         all_comps_copy.push_back(comp_copy);
52     }
53 
54     // Now call update component
55     mb->rpc->notify_with_token(
56         MPMD_DEFAULT_INIT_TIMEOUT, "update_component", all_metadata, all_data);
57     return all_comps_copy;
58 }
59 
60 /*
61  * Query the device to get the metadata for desired component
62  *
63  * \param comp_name String component name
64  * \param mb Reference to the actual device
65  * \return component files containing the component metadata
66  */
_get_component_info(const std::string & comp_name,mpmd_mboard_impl * mb)67 uhd::usrp::component_files_t _get_component_info(
68     const std::string& comp_name, mpmd_mboard_impl* mb)
69 {
70     UHD_LOG_TRACE("MPMD", "Getting component info for " << comp_name);
71     const auto component_metadata = mb->rpc->request<std::map<std::string, std::string>>(
72         "get_component_info", comp_name);
73     // Copy the contents of the component metadata into a object we can return
74     uhd::usrp::component_file_t return_component;
75     auto& return_metadata = return_component.metadata;
76     for (auto item : component_metadata) {
77         return_metadata[item.first] = item.second;
78     }
79     return uhd::usrp::component_files_t{return_component};
80 }
81 } // namespace
82 
init_property_tree(uhd::property_tree::sptr tree,fs_path mb_path,mpmd_mboard_impl * mb)83 void mpmd_impl::init_property_tree(
84     uhd::property_tree::sptr tree, fs_path mb_path, mpmd_mboard_impl* mb)
85 {
86     /*** Device info ****************************************************/
87     if (not tree->exists("/name")) {
88         tree->create<std::string>("/name").set(
89             mb->device_info.get("description", "Unknown MPM device"));
90     }
91     tree->create<std::string>(mb_path / "name")
92         .set(mb->device_info.get("name", "UNKNOWN"));
93     tree->create<std::string>(mb_path / "serial")
94         .set(mb->device_info.get("serial", "n/a"));
95     tree->create<std::string>(mb_path / "connection")
96         .set(mb->device_info.get("connection", "UNKNOWN"));
97     tree->create<size_t>(mb_path / "link_max_rate").set(125000000);
98     tree->create<std::string>(mb_path / "mpm_version")
99         .set(mb->device_info.get("mpm_version", "UNKNOWN"));
100     tree->create<std::string>(mb_path / "fpga_version")
101         .set(mb->device_info.get("fpga_version", "UNKNOWN"));
102     tree->create<std::string>(mb_path / "fpga_version_hash")
103         .set(mb->device_info.get("fpga_version_hash", "UNKNOWN"));
104 
105     /*** Clocking *******************************************************/
106     tree->create<std::string>(mb_path / "clock_source/value")
107         .add_coerced_subscriber([mb](const std::string& clock_source) {
108             mb->rpc->notify_with_token(
109                 MPMD_DEFAULT_INIT_TIMEOUT, "set_clock_source", clock_source);
110         })
111         .set_publisher([mb]() {
112             return mb->rpc->request_with_token<std::string>("get_clock_source");
113         });
114     tree->create<std::vector<std::string>>(mb_path / "clock_source/options")
115         .set_publisher([mb]() {
116             return mb->rpc->request_with_token<std::vector<std::string>>(
117                 "get_clock_sources");
118         });
119     tree->create<std::string>(mb_path / "time_source/value")
120         .add_coerced_subscriber([mb](const std::string& time_source) {
121             mb->rpc->notify_with_token(
122                 MPMD_DEFAULT_INIT_TIMEOUT, "set_time_source", time_source);
123         })
124         .set_publisher([mb]() {
125             return mb->rpc->request_with_token<std::string>("get_time_source");
126         });
127     tree->create<std::vector<std::string>>(mb_path / "time_source/options")
128         .set_publisher([mb]() {
129             return mb->rpc->request_with_token<std::vector<std::string>>(
130                 "get_time_sources");
131         });
132 
133     /*** Sensors ********************************************************/
134     auto sensor_list =
135         mb->rpc->request_with_token<std::vector<std::string>>("get_mb_sensors");
136     UHD_LOG_DEBUG("MPMD", "Found " << sensor_list.size() << " motherboard sensors.");
137     for (const auto& sensor_name : sensor_list) {
138         UHD_LOG_TRACE("MPMD", "Adding motherboard sensor `" << sensor_name << "'");
139         tree->create<sensor_value_t>(mb_path / "sensors" / sensor_name)
140             .set_publisher([mb, sensor_name]() {
141                 auto sensor_val = sensor_value_t(
142                     mb->rpc->request_with_token<sensor_value_t::sensor_map_t>(
143                         MPMD_DEFAULT_INIT_TIMEOUT, "get_mb_sensor", sensor_name));
144                 return sensor_val;
145             })
146             .set_coercer([](const sensor_value_t&) {
147                 throw uhd::runtime_error("Trying to write read-only sensor value!");
148                 return sensor_value_t("", "", "");
149             });
150     }
151 
152     /*** EEPROM *********************************************************/
153     tree->create<uhd::usrp::mboard_eeprom_t>(mb_path / "eeprom")
154         .add_coerced_subscriber([mb](const uhd::usrp::mboard_eeprom_t& mb_eeprom) {
155             eeprom_map_t eeprom_map;
156             for (const auto& key : mb_eeprom.keys()) {
157                 eeprom_map[key] =
158                     std::vector<uint8_t>(mb_eeprom[key].cbegin(), mb_eeprom[key].cend());
159             }
160             mb->rpc->notify_with_token(
161                 MPMD_DEFAULT_INIT_TIMEOUT, "set_mb_eeprom", eeprom_map);
162         })
163         .set_publisher([mb]() {
164             auto mb_eeprom =
165                 mb->rpc->request_with_token<std::map<std::string, std::string>>(
166                     "get_mb_eeprom");
167             uhd::usrp::mboard_eeprom_t mb_eeprom_dict(
168                 mb_eeprom.cbegin(), mb_eeprom.cend());
169             return mb_eeprom_dict;
170         });
171 
172     /*** Updateable Components ******************************************/
173     std::vector<std::string> updateable_components =
174         mb->rpc->request<std::vector<std::string>>("list_updateable_components");
175     // TODO: Check the 'id' against the registered property
176     UHD_LOG_DEBUG("MPMD",
177         "Found " << updateable_components.size()
178                  << " updateable motherboard components.");
179     for (const auto& comp_name : updateable_components) {
180         UHD_LOG_TRACE("MPMD", "Adding motherboard component: " << comp_name);
181         tree->create<uhd::usrp::component_files_t>(mb_path / "components" / comp_name)
182             .set_coercer([mb, comp_name](const uhd::usrp::component_files_t& comp_files) {
183                 auto comp_info = _get_component_info(comp_name, mb)[0];
184                 if (comp_info.metadata.get("reset", "") == "True") {
185                     UHD_LOG_DEBUG(
186                         "MPMD", "Bracing for potential loss of RPC server connection.");
187                     mb->allow_claim_failure(true);
188                 }
189                 auto result = _update_component(comp_files, mb);
190                 mb->allow_claim_failure(false);
191                 return result;
192             })
193             .set_publisher([mb, comp_name]() {
194                 return _get_component_info(comp_name, mb);
195             }); // Done adding component to property tree
196     }
197 }
198