1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
//
// Copyright 2017 Ettus Research, a National Instruments Company
// Copyright 2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
#include "mpmd_impl.hpp"
#include <uhd/config.hpp>
#include <uhd/device.hpp>
#include <uhd/exception.hpp>
#include <uhd/image_loader.hpp>
#include <uhd/types/component_file.hpp>
#include <uhd/types/eeprom.hpp>
#include <uhd/utils/paths.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem/convenience.hpp>
#include <fstream>
#include <iterator>
#include <sstream>
#include <streambuf>
#include <string>
using namespace uhd;
namespace uhd { namespace /*anon*/ {
const size_t MD5LEN = 32; // Length of a MD5 hash in chars
/*
* Helper function to generate a component_file_t using the input ID and path to file.
*/
uhd::usrp::component_file_t generate_component(
const std::string& id, const std::string& filepath)
{
uhd::usrp::component_file_t component_file;
// Add an ID to the metadata
component_file.metadata["id"] = id;
UHD_LOG_TRACE(
"MPMD IMAGE LOADER", "Component ID added to the component dictionary: " << id);
// Add the filename to the metadata
// Remove the path to the filename
component_file.metadata["filename"] =
boost::filesystem::path(filepath).filename().string();
UHD_LOG_TRACE("MPMD IMAGE LOADER",
"Component filename added to the component dictionary: " << filepath);
// Add the hash, if a hash file exists
const std::string component_hash_filepath = filepath + ".md5";
std::ifstream component_hash_ifstream(
component_hash_filepath.c_str(), std::ios::binary);
std::string component_hash;
if (component_hash_ifstream.is_open()) {
// TODO: Verify that the hash read is valid, ie only contains 0-9a-f.
component_hash.resize(MD5LEN);
component_hash_ifstream.read(&component_hash[0], MD5LEN);
component_hash_ifstream.close();
component_file.metadata["md5"] = component_hash;
UHD_LOG_TRACE("MPMD IMAGE LOADER",
"Added component file hash to the component dictionary.");
} else {
// If there is no hash file, don't worry about it too much
UHD_LOG_DEBUG("MPMD IMAGE LOADER",
"Could not open component file hash file: " << component_hash_filepath);
}
// Read the component file image into a structure suitable to sent as a binary string
// to MPM
std::vector<uint8_t> data;
std::ifstream component_ifstream(filepath.c_str(), std::ios::binary);
if (component_ifstream.is_open()) {
data.insert(data.begin(),
std::istreambuf_iterator<char>(component_ifstream),
std::istreambuf_iterator<char>());
component_ifstream.close();
} else {
const std::string err_msg("Component file does not exist: " + filepath);
throw uhd::runtime_error(err_msg);
}
component_file.data = data;
return component_file;
}
/*
* Helper function to generate a component_file_t using the input ID and contents.
*/
uhd::usrp::component_file_t generate_component(const std::string& id,
const std::vector<uint8_t>& contents,
const uhd::dict<std::string, std::string>& metadata)
{
uhd::usrp::component_file_t component_file;
// Add an ID to the metadata
component_file.metadata["id"] = id;
component_file.metadata.update(metadata);
UHD_LOG_TRACE(
"MPMD IMAGE LOADER", "Component ID added to the component dictionary: " << id);
component_file.data = contents;
return component_file;
}
/*
* Function to be registered with uhd_image_loader
*/
static bool mpmd_image_loader(const image_loader::image_loader_args_t& image_loader_args)
{
// See if any MPM devices with the given args are found
device_addr_t find_hint = image_loader_args.args;
find_hint.set("find_all", "1"); // We need to find all devices
device_addrs_t devs = mpmd_find(find_hint);
if (devs.size() != 1) {
// TODO: Do we want to handle multiple devices here?
// Or force uhd_image_loader to only feed us a single device?
return false;
}
// Grab the first device_addr and add the option to skip initialization
device_addr_t dev_addr(devs[0]);
dev_addr["skip_init"] = "1";
// Make the device
uhd::device::sptr usrp = uhd::device::make(dev_addr, uhd::device::USRP);
uhd::property_tree::sptr tree = usrp->get_tree();
// Generate the component files
uhd::usrp::component_files_t all_component_files;
// check if a component was provided
if (not image_loader_args.id.empty() && not image_loader_args.component.empty()) {
uhd::usrp::component_file_t component = generate_component(image_loader_args.id,
image_loader_args.component,
image_loader_args.metadata);
all_component_files.push_back(component);
} else {
// FPGA component struct
const auto fpga_path = [image_loader_args, dev_addr, tree]() -> std::string {
// If the user provided a path to an fpga image, use that
if (not image_loader_args.fpga_path.empty()) {
if (boost::filesystem::exists(image_loader_args.fpga_path)) {
return image_loader_args.fpga_path;
} else {
throw uhd::runtime_error(
str(boost::format("FPGA file provided does not exist: %s")
% image_loader_args.fpga_path));
}
}
// Otherwise, we need to generate one
else {
/*
* The user can specify an FPGA type (HG, XG, AA), rather than a filename.
* If the user does not specify one, this will default to the type
* currently on the device. If this cannot be determined, then the user is
* forced to specify a filename.
*/
const auto fpga_type = [image_loader_args, tree]() -> std::string {
// If the user didn't provide a type, use the type of currently loaded
// image on the device
if (image_loader_args.args.has_key("fpga")) {
return image_loader_args.args.get("fpga");
} else if (tree->exists("/mboards/0/components/fpga")) {
// Pull the FPGA info from the property tree
// The getter should return a vector of a single component_file_t,
// so grab the metadata from that
auto fpga_metadata = tree->access<uhd::usrp::component_files_t>(
"/mboards/0/components/fpga")
.get()[0]
.metadata;
return fpga_metadata.get("type", "");
// TODO: Do we want to pull the type from the filename if its not
// available in the metadata directly?
}
return "";
}(); // generate_fpga_type lambda function
UHD_LOG_TRACE("MPMD IMAGE LOADER", "FPGA type: " << fpga_type);
if (!dev_addr.has_key("product")) {
throw uhd::runtime_error(
"Found a device but could not auto-generate an image filename.");
} else if (fpga_type == "") {
return find_image_path(
str(boost::format("usrp_%s_fpga.bit")
% (boost::algorithm::to_lower_copy(dev_addr["product"]))));
} else {
return find_image_path(
str(boost::format("usrp_%s_fpga_%s.bit")
% (boost::algorithm::to_lower_copy(dev_addr["product"]))
% fpga_type));
}
}
}(); // generate_fpga_path lambda function
UHD_LOG_TRACE("MPMD IMAGE LOADER", "FPGA path: " << fpga_path);
uhd::usrp::component_file_t comp_fpga = generate_component("fpga", fpga_path);
all_component_files.push_back(comp_fpga);
// DTS component struct
// First, we need to determine the name
const std::string base_name =
boost::filesystem::change_extension(fpga_path, "").string();
if (base_name == fpga_path) {
const std::string err_msg(
"Can't cut extension from FPGA filename... " + fpga_path);
throw uhd::runtime_error(err_msg);
}
const std::string dts_path = base_name + ".dts";
// Then try to generate it
try {
uhd::usrp::component_file_t comp_dts = generate_component("dts", dts_path);
all_component_files.push_back(comp_dts);
UHD_LOG_TRACE("MPMD IMAGE LOADER", "FPGA and DTS images read from file.");
} catch (const uhd::runtime_error& ex) {
// If we can't find the DTS file, that's fine, continue without it
UHD_LOG_WARNING("MPMD IMAGE LOADER", ex.what());
UHD_LOG_TRACE("MPMD IMAGE LOADER", "FPGA images read from file.");
}
}
// Call RPC to update the component
UHD_LOG_INFO("MPMD IMAGE LOADER", "Starting update. This may take a while.");
tree->access<uhd::usrp::component_files_t>("/mboards/0/components/fpga")
.set(all_component_files);
UHD_LOG_INFO("MPMD IMAGE LOADER", "Update component function succeeded.");
return true;
}
}} // namespace uhd::
UHD_STATIC_BLOCK(register_mpm_image_loader)
{
// TODO: Update recovery instructions
const std::string recovery_instructions =
"Aborting. Your USRP MPM-enabled device's update may or may not have\n"
"completed. The contents of the image files may have been corrupted.\n"
"Please verify those files as soon as possible.";
// TODO: 'n3xx' doesn't really fit the MPM abstraction, but this is simpler for the
// time being
image_loader::register_image_loader("n3xx", mpmd_image_loader, recovery_instructions);
image_loader::register_image_loader("e3xx", mpmd_image_loader, recovery_instructions);
}
|