radio_tool 0.2.1
Loading...
Searching...
No Matches
radio_tool.cpp
1
18#include <radio_tool/radio/radio_factory.hpp>
19#include <radio_tool/fw/fw_factory.hpp>
20#include <radio_tool/codeplug/codeplug_factory.hpp>
21
22#include <radio_tool/radio/tyt_radio.hpp>
23#include <radio_tool/dfu/dfu_exception.hpp>
24#include <radio_tool/util.hpp>
25#include <radio_tool/version.hpp>
26
27#ifdef XOR_TOOL
28#include <xor_tool.hpp>
29#endif
30
31#include <iostream>
32#include <filesystem>
33#include <cxxopts.hpp>
34#include <fstream>
35
36using namespace radio_tool::fw;
37using namespace radio_tool::radio;
38using namespace radio_tool::codeplug;
39
40template <class T>
41auto GetOptionOrErr(const cxxopts::ParseResult &cmd, const std::string &v, const std::string &err) -> const T &
42{
43 if (cmd.count(v))
44 {
45 return cmd[v].as<T>();
46 }
47 else
48 {
49 throw std::runtime_error(err);
50 }
51}
52
53int main(int argc, char **argv)
54{
55 try
56 {
57 std::stringstream ssVersion;
58 ssVersion << g_PROJECT_NAME << " v" << g_PROJECT_VERSION << "-" << g_GIT_SHA1_SHORT;
59 auto version = ssVersion.str();
60
61 cxxopts::Options options(argv[0], version);
62
63 // clang-format off
64 options.add_options("General")
65 ("h,help", "Show this message", cxxopts::value<std::string>()->implicit_value(""), "<command>")
66 ("l,list", "List devices")
67 ("d,device", "Device to use", cxxopts::value<uint16_t>(), "<index>")
68 ("i,in", "Input file", cxxopts::value<std::string>(), "<file>")
69 ("o,out", "Output file", cxxopts::value<std::string>(), "<file>");
70
71 options.add_options("Programming")
72 ("f,flash", "Flash firmware")
73 ("p,program", "Upload codeplug");
74
75 options.add_options("All radio")
76 ("info", "Print some info about the radio")
77 ("get-status", "Print the current DFU Status");
78
79 options.add_options("TYT Radio")
80 ("get-time", "Gets the radio time")
81 ("set-time", "Sets the radio time")
82 ("dump-reg", "Dump a register from the radio", cxxopts::value<uint16_t>(), "<register>")
83 ("reboot", "Reboot the radio")
84 ("dump-bootloader", "Dump bootloader (Mac only)");
85
86 options.add_options("Firmware")
87 ("fw-info", "Print info about a firmware file")
88 ("wrap", "Wrap a firmware bin (use --help wrap, for more info)")
89 ("unwrap", "Unwrap a fimrware file")
90#ifdef XOR_TOOL
91 ("make-xor", "Try to make an XOR key for the input firmware");
92#else
93 ;
94#endif
95
96 options.add_options("Codeplug")
97 ("codeplug-info", "Print info about a codeplug file");
98
99 options.add_options("Wrap")
100 ("s,segment", "Add a segment for wrapping", cxxopts::value<std::vector<std::string>>(), "<0x08000000:region_0.bin>")
101 ("r,radio", "Radio to build firmware file for", cxxopts::value<std::string>(), "<DM1701>");
102
103 auto cmd = options.parse(argc, argv);
104 // clang-format on
105
106 if (cmd.count("help") || cmd.arguments().empty())
107 {
108 std::vector<std::string> help_groups;
109
110 auto section = cmd.count("help") ? cmd["help"].as<std::string>() : std::string();
111 if (!section.empty())
112 {
113 for (const auto &g : options.groups())
114 {
115 if (g.size() != section.size())
116 continue;
117 if (std::equal(g.begin(), g.end(), section.begin(), [](char a, char b)
118 { return std::toupper(a) == std::toupper(b); }))
119 {
120 help_groups.push_back(g);
121 }
122 }
123 }
124 else
125 {
126 help_groups = {"General", "Programming", "Firmware", "All radio", "TYT Radio", "Codeplug"};
127 }
128 std::cerr << options.help(help_groups) << std::endl;
129 exit(0);
130 }
131
132 if (cmd.count("list-radios"))
133 {
134 //TODO: list radio models supported
135 exit(0);
136 }
137
138 //do non device specific commands
139 if (cmd.count("fw-info"))
140 {
141 auto file = GetOptionOrErr<std::string>(cmd, "in", "Input file not specified");
142
144 fw->Read(file);
145 std::cerr << fw->ToString();
146 exit(0);
147 }
148
149 if (cmd.count("codeplug-info"))
150 {
151 auto file = GetOptionOrErr<std::string>(cmd, "in", "Input file not specified");
152
153 auto h = CodeplugFactory::GetCodeplugHandler(file);
154 h->Read(file);
155 std::cerr << h->ToString();
156 exit(0);
157 }
158
159 if (cmd.count("wrap"))
160 {
161 auto out = GetOptionOrErr<std::string>(cmd, "out", "Output file not specified");
162 auto radio = GetOptionOrErr<std::string>(cmd, "radio", "Radio not specified");
163 auto segments = GetOptionOrErr<std::vector<std::string>>(cmd, "segment", "Must specify at least 1 segment");
164
166 fw->SetRadioModel(radio);
167 for (const auto &sx : segments)
168 {
169 auto schar = sx.find(':');
170 if (schar != sx.npos)
171 {
172 uint32_t addr = 0;
173 auto addr_str = sx.substr(0, schar);
174 if (addr_str.find("0x") != addr_str.npos)
175 {
176 addr = std::stoi(addr_str, 0, 16);
177 }
178 else
179 {
180 addr = std::stoi(addr_str, 0, 10);
181 }
182
183 auto filename = sx.substr(schar + 1);
184 std::cerr << "Adding segment 0x"
185 << std::hex << std::setw(8) << std::setfill('0') << addr
186 << " from file " << filename << std::endl;
187
188 std::ifstream f_seg(filename, std::ios_base::binary);
189 if (f_seg.is_open())
190 {
191 f_seg.seekg(0, f_seg.end);
192 auto len = f_seg.tellg();
193 f_seg.seekg(0, f_seg.beg);
194
195 std::vector<uint8_t> seg_data;
196 seg_data.resize(len);
197 f_seg.read((char *)seg_data.data(), len);
198 f_seg.close();
199
200 fw->AppendSegment(addr, seg_data);
201 }
202 else
203 {
204 throw std::runtime_error("Cant open file for segment");
205 }
206 }
207 else
208 {
209 throw std::invalid_argument("Segments must be in the format '0x0000:filename.bin'");
210 }
211 }
212
213 fw->Encrypt();
214 fw->Write(out);
215 std::cerr << "Done!" << std::endl;
216 exit(0);
217 }
218
219 if (cmd.count("unwrap"))
220 {
221 auto in_file = GetOptionOrErr<std::string>(cmd, "in", "Input file not specified");
222 auto out_file = GetOptionOrErr<std::string>(cmd, "out", "Output file not specified");
223
224 auto fw_handler = FirmwareFactory::GetFirmwareFileHandler(in_file);
225 fw_handler->Read(in_file);
226 fw_handler->Decrypt();
227
228 for (const auto &rn : fw_handler->GetDataSegments())
229 {
230 std::stringstream ss_name;
231 ss_name << out_file << "_0x" << std::setw(8) << std::setfill('0') << std::hex << rn.address;
232
233 std::ofstream fw_out;
234 fw_out.open(ss_name.str(), std::ios_base::out | std::ios_base::binary);
235 if (fw_out.is_open())
236 {
237 fw_out.write((const char *)rn.data.data(), rn.data.size());
238 fw_out.close();
239 }
240 else
241 {
242 std::cerr << "Failed to open output file: " << out_file << std::endl;
243 exit(1);
244 }
245 }
246 exit(0);
247 }
248
249#ifdef XOR_TOOL
250 if (cmd.count("make-xor"))
251 {
252 auto in_file = GetOptionOrErr<std::string>(cmd, "in", "Input file not specified");
253 auto fw_handler = FirmwareFactory::GetFirmwareFileHandler(in_file);
254 fw_handler->Read(in_file);
255
256 auto key = radio_tool::fw::XORTool::MakeXOR(fw_handler->GetData());
257 for (const auto &region : fw_handler->GetDataSegments())
258 {
259 if (radio_tool::fw::XORTool::Verify(region.address, region.data, key))
260 {
261 std::cout
262 << "Region @ 0x" << std::setfill('0') << std::setw(8) << std::hex << region.address
263 << " appears to be a valid vector_table" << std::endl;
264 }
265 }
266
267 radio_tool::PrintHex(key.begin(), key.end());
268
269 // dump in C format
270 std::cout << "const uint8_t[] key = {";
271 for(const auto &v : key) {
272 std::cout << "0x" << std::hex << std::setfill('0') << std::setw(2) << (int)v << ",";
273 }
274 std::cout << "};" << std::endl;
275 exit(0);
276 }
277#endif
278
279 auto rdFactory = RadioFactory();
280 if (cmd.count("list"))
281 {
282 for (const auto &d : rdFactory.ListDevices())
283 {
284 std::wcout << d->ToString() << std::endl;
285 }
286 exit(0);
287 }
288
289 if (!cmd.count("device"))
290 {
291 std::cout << "You must select a device" << std::endl;
292 exit(1);
293 }
294
295 auto index = cmd["device"].as<uint16_t>();
296 auto radio = rdFactory.OpenDevice(index);
297
298 if (cmd.count("info"))
299 {
300 std::cout << radio->ToString() << std::endl;
301 exit(0);
302 }
303
304 if (cmd.count("flash"))
305 {
306 auto in_file = GetOptionOrErr<std::string>(cmd, "in", "Input file not specified");
307 radio->WriteFirmware(in_file);
308 std::cout << "Done!" << std::endl;
309 exit(0);
310 }
311
312 if (cmd.count("program"))
313 {
314 }
315 }
316 catch (const radio_tool::dfu::DFUException &dfuEx)
317 {
318 std::cerr << "DFU Error: " << dfuEx.what() << std::endl;
319 exit(1);
320 }
321 catch (const cxxopts::OptionException &e)
322 {
323 std::cerr << "error parsing options: " << e.what() << std::endl;
324 exit(1);
325 }
326 catch (const std::exception &gex)
327 {
328 std::cerr << "Error: " << gex.what() << std::endl;
329 exit(1);
330 }
331}
332
333auto tytCommands(const cxxopts::ParseResult &cmd, RadioOperations *radio) -> void
334{
335 if (typeid(radio) == typeid(radio_tool::radio::TYTRadio))
336 {
337 std::cerr << "Cant use TYT commands on non-tyt radio!" << std::endl;
338 exit(1);
339 }
340
341 auto tyt_radio = dynamic_cast<radio_tool::radio::TYTRadio *>(radio);
342 auto dfu = tyt_radio->GetDFU();
343
344 if (cmd.count("get-status"))
345 {
346 auto status = dfu->GetStatus();
347 std::cerr << status.ToString() << std::endl;
348 }
349
350 if (cmd.count("dump-reg"))
351 {
352 auto x = cmd["dump-reg"].as<uint16_t>();
353 std::cerr << "Read register: 0x" << std::setfill('0') << std::setw(2) << std::hex << x << std::endl;
354 //radio_tool::PrintHex(dfu.ReadRegister(static_cast<const TYTRegister>(x)));
355 }
356
357 if (cmd.count("dump-bootloader"))
358 {
359 auto out_file = GetOptionOrErr<std::string>(cmd, "out", "Input file not specified");
360 auto size = 0xc000;
361 std::ofstream outf;
362 outf.open(out_file, std::ios_base::out | std::ios_base::binary);
363 if (outf.is_open())
364 {
365 auto mem = dfu->Upload(size, 2);
366 //radio_tool::PrintHex(mem);
367 outf.write((char *)mem.data(), mem.size());
368 outf.close();
369 }
370 else
371 {
372 std::cerr << "Failed to open output file: " << out_file << std::endl;
373 exit(1);
374 }
375 }
376
377 if (cmd.count("get-time"))
378 {
379 //auto tm = dfu.GetTime();
380 //std::cerr << ctime(&tm);
381 }
382
383 if (cmd.count("set-time"))
384 {
385 //dfu.SetTime();
386 }
387
388 if (cmd.count("reboot"))
389 {
390 //dfu.Reboot();
391 }
392}
static auto GetFirmwareModelHandler(const std::string &model) -> std::unique_ptr< FirmwareSupport >
Definition: fw_factory.hpp:86
static auto GetFirmwareFileHandler(const std::string &file) -> std::unique_ptr< FirmwareSupport >
Definition: fw_factory.hpp:70
auto GetDFU() const -> const dfu::TYTDFU *
Definition: tyt_radio.hpp:45