1*61c4878aSAndroid Build Coastguard Worker // Copyright 2024 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Worker #include <errno.h>
16*61c4878aSAndroid Build Coastguard Worker #include <fcntl.h>
17*61c4878aSAndroid Build Coastguard Worker #include <getopt.h>
18*61c4878aSAndroid Build Coastguard Worker #include <string.h>
19*61c4878aSAndroid Build Coastguard Worker #include <unistd.h>
20*61c4878aSAndroid Build Coastguard Worker
21*61c4878aSAndroid Build Coastguard Worker #include <cctype>
22*61c4878aSAndroid Build Coastguard Worker #include <charconv>
23*61c4878aSAndroid Build Coastguard Worker #include <fstream>
24*61c4878aSAndroid Build Coastguard Worker #include <iomanip>
25*61c4878aSAndroid Build Coastguard Worker #include <iostream>
26*61c4878aSAndroid Build Coastguard Worker #include <optional>
27*61c4878aSAndroid Build Coastguard Worker #include <string>
28*61c4878aSAndroid Build Coastguard Worker #include <string_view>
29*61c4878aSAndroid Build Coastguard Worker #include <vector>
30*61c4878aSAndroid Build Coastguard Worker
31*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
32*61c4878aSAndroid Build Coastguard Worker #include "pw_preprocessor/util.h"
33*61c4878aSAndroid Build Coastguard Worker #include "pw_result/result.h"
34*61c4878aSAndroid Build Coastguard Worker #include "pw_spi_linux/spi.h"
35*61c4878aSAndroid Build Coastguard Worker #include "pw_status/status.h"
36*61c4878aSAndroid Build Coastguard Worker
37*61c4878aSAndroid Build Coastguard Worker namespace pw::spi {
38*61c4878aSAndroid Build Coastguard Worker namespace {
39*61c4878aSAndroid Build Coastguard Worker
40*61c4878aSAndroid Build Coastguard Worker constexpr unsigned int kDefaultMode = 0;
41*61c4878aSAndroid Build Coastguard Worker constexpr unsigned int kDefaultBits = 8;
42*61c4878aSAndroid Build Coastguard Worker
43*61c4878aSAndroid Build Coastguard Worker const struct option kLongOptions[] = {
44*61c4878aSAndroid Build Coastguard Worker {"bits", required_argument, nullptr, 'b'},
45*61c4878aSAndroid Build Coastguard Worker {"device", required_argument, nullptr, 'D'},
46*61c4878aSAndroid Build Coastguard Worker {"freq", required_argument, nullptr, 'F'},
47*61c4878aSAndroid Build Coastguard Worker {"human", no_argument, nullptr, 'h'},
48*61c4878aSAndroid Build Coastguard Worker {"input", required_argument, nullptr, 'i'},
49*61c4878aSAndroid Build Coastguard Worker {"lsb", no_argument, nullptr, 'l'},
50*61c4878aSAndroid Build Coastguard Worker {"mode", required_argument, nullptr, 'm'},
51*61c4878aSAndroid Build Coastguard Worker {"output", required_argument, nullptr, 'o'},
52*61c4878aSAndroid Build Coastguard Worker {"rx-count", required_argument, nullptr, 'r'},
53*61c4878aSAndroid Build Coastguard Worker {}, // terminator
54*61c4878aSAndroid Build Coastguard Worker };
55*61c4878aSAndroid Build Coastguard Worker
56*61c4878aSAndroid Build Coastguard Worker // Starts with ':' to skip printing errors and return ':' for a missing option
57*61c4878aSAndroid Build Coastguard Worker // argument.
58*61c4878aSAndroid Build Coastguard Worker const char* kShortOptions = ":b:D:F:hi:lm:o:r:";
59*61c4878aSAndroid Build Coastguard Worker
Usage()60*61c4878aSAndroid Build Coastguard Worker void Usage() {
61*61c4878aSAndroid Build Coastguard Worker std::cerr << "Usage: pw_spi_linux_cli -D DEVICE -F FREQ [flags]" << std::endl;
62*61c4878aSAndroid Build Coastguard Worker std::cerr << "Required flags:" << std::endl;
63*61c4878aSAndroid Build Coastguard Worker std::cerr << " -D/--device SPI device path (e.g. /dev/spidev0.0"
64*61c4878aSAndroid Build Coastguard Worker << std::endl;
65*61c4878aSAndroid Build Coastguard Worker std::cerr << " -F/--freq SPI clock frequency in Hz (e.g. 24000000)"
66*61c4878aSAndroid Build Coastguard Worker << std::endl;
67*61c4878aSAndroid Build Coastguard Worker std::cerr << std::endl;
68*61c4878aSAndroid Build Coastguard Worker std::cerr << "Optional flags:" << std::endl;
69*61c4878aSAndroid Build Coastguard Worker std::cerr << " -b/--bits Bits per word, default: " << kDefaultBits
70*61c4878aSAndroid Build Coastguard Worker << std::endl;
71*61c4878aSAndroid Build Coastguard Worker std::cerr << " -h/--human Human-readable output (default: binary, "
72*61c4878aSAndroid Build Coastguard Worker "unless output to stdout tty)"
73*61c4878aSAndroid Build Coastguard Worker << std::endl;
74*61c4878aSAndroid Build Coastguard Worker std::cerr << " -i/--input Input file, or - for stdin" << std::endl;
75*61c4878aSAndroid Build Coastguard Worker std::cerr << " If not given, no data is sent." << std::endl;
76*61c4878aSAndroid Build Coastguard Worker std::cerr << " -l/--lsb LSB first (default: MSB first)" << std::endl;
77*61c4878aSAndroid Build Coastguard Worker std::cerr << " -m/--mode SPI mode (0-3), default: " << kDefaultMode
78*61c4878aSAndroid Build Coastguard Worker << std::endl;
79*61c4878aSAndroid Build Coastguard Worker std::cerr << " -o/--output Output file (default: stdout)" << std::endl;
80*61c4878aSAndroid Build Coastguard Worker std::cerr << " -r/--rx-count Number of bytes to receive (defaults to size "
81*61c4878aSAndroid Build Coastguard Worker "of input)"
82*61c4878aSAndroid Build Coastguard Worker << std::endl;
83*61c4878aSAndroid Build Coastguard Worker }
84*61c4878aSAndroid Build Coastguard Worker
85*61c4878aSAndroid Build Coastguard Worker struct Args {
86*61c4878aSAndroid Build Coastguard Worker std::string device;
87*61c4878aSAndroid Build Coastguard Worker unsigned int frequency = 0;
88*61c4878aSAndroid Build Coastguard Worker std::optional<std::string> input_path;
89*61c4878aSAndroid Build Coastguard Worker std::string output_path = "-";
90*61c4878aSAndroid Build Coastguard Worker bool human_readable = false;
91*61c4878aSAndroid Build Coastguard Worker std::optional<unsigned int> rx_count;
92*61c4878aSAndroid Build Coastguard Worker
93*61c4878aSAndroid Build Coastguard Worker unsigned int mode = kDefaultMode;
94*61c4878aSAndroid Build Coastguard Worker unsigned int bits = kDefaultBits;
95*61c4878aSAndroid Build Coastguard Worker bool lsb_first = false;
96*61c4878aSAndroid Build Coastguard Worker
GetSpiConfigpw::spi::__anona050ae370111::Args97*61c4878aSAndroid Build Coastguard Worker Config GetSpiConfig() const {
98*61c4878aSAndroid Build Coastguard Worker return {
99*61c4878aSAndroid Build Coastguard Worker .polarity = (mode & 0b10) ? ClockPolarity::kActiveLow
100*61c4878aSAndroid Build Coastguard Worker : ClockPolarity::kActiveHigh,
101*61c4878aSAndroid Build Coastguard Worker .phase =
102*61c4878aSAndroid Build Coastguard Worker (mode & 0b01) ? ClockPhase::kFallingEdge : ClockPhase::kRisingEdge,
103*61c4878aSAndroid Build Coastguard Worker .bits_per_word = bits,
104*61c4878aSAndroid Build Coastguard Worker .bit_order = lsb_first ? BitOrder::kLsbFirst : BitOrder::kMsbFirst,
105*61c4878aSAndroid Build Coastguard Worker };
106*61c4878aSAndroid Build Coastguard Worker }
107*61c4878aSAndroid Build Coastguard Worker };
108*61c4878aSAndroid Build Coastguard Worker
109*61c4878aSAndroid Build Coastguard Worker template <class T>
ParseNumber(std::string_view str)110*61c4878aSAndroid Build Coastguard Worker std::optional<T> ParseNumber(std::string_view str) {
111*61c4878aSAndroid Build Coastguard Worker T value{};
112*61c4878aSAndroid Build Coastguard Worker const auto* str_end = str.data() + str.size();
113*61c4878aSAndroid Build Coastguard Worker auto [ptr, ec] = std::from_chars(str.data(), str_end, value);
114*61c4878aSAndroid Build Coastguard Worker if (ec == std::errc() && ptr == str_end) {
115*61c4878aSAndroid Build Coastguard Worker return value;
116*61c4878aSAndroid Build Coastguard Worker }
117*61c4878aSAndroid Build Coastguard Worker return std::nullopt;
118*61c4878aSAndroid Build Coastguard Worker }
119*61c4878aSAndroid Build Coastguard Worker
ParseArgs(int argc,char * argv[])120*61c4878aSAndroid Build Coastguard Worker Result<Args> ParseArgs(int argc, char* argv[]) {
121*61c4878aSAndroid Build Coastguard Worker Args args;
122*61c4878aSAndroid Build Coastguard Worker bool human_readable_given;
123*61c4878aSAndroid Build Coastguard Worker
124*61c4878aSAndroid Build Coastguard Worker while (true) {
125*61c4878aSAndroid Build Coastguard Worker int current_optind = optind;
126*61c4878aSAndroid Build Coastguard Worker int c = getopt_long(argc, argv, kShortOptions, kLongOptions, nullptr);
127*61c4878aSAndroid Build Coastguard Worker if (c == -1) {
128*61c4878aSAndroid Build Coastguard Worker break;
129*61c4878aSAndroid Build Coastguard Worker }
130*61c4878aSAndroid Build Coastguard Worker
131*61c4878aSAndroid Build Coastguard Worker switch (c) {
132*61c4878aSAndroid Build Coastguard Worker case 'b': {
133*61c4878aSAndroid Build Coastguard Worker auto bits = ParseNumber<unsigned int>(optarg);
134*61c4878aSAndroid Build Coastguard Worker if (bits > 32) {
135*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Invalid bits : %s", optarg);
136*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
137*61c4878aSAndroid Build Coastguard Worker }
138*61c4878aSAndroid Build Coastguard Worker args.bits = bits.value();
139*61c4878aSAndroid Build Coastguard Worker break;
140*61c4878aSAndroid Build Coastguard Worker }
141*61c4878aSAndroid Build Coastguard Worker case 'D':
142*61c4878aSAndroid Build Coastguard Worker args.device = optarg;
143*61c4878aSAndroid Build Coastguard Worker break;
144*61c4878aSAndroid Build Coastguard Worker case 'F': {
145*61c4878aSAndroid Build Coastguard Worker auto freq = ParseNumber<unsigned int>(optarg);
146*61c4878aSAndroid Build Coastguard Worker if (!freq || freq.value() == 0) {
147*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Invalid frequency: %s", optarg);
148*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
149*61c4878aSAndroid Build Coastguard Worker }
150*61c4878aSAndroid Build Coastguard Worker args.frequency = freq.value();
151*61c4878aSAndroid Build Coastguard Worker break;
152*61c4878aSAndroid Build Coastguard Worker }
153*61c4878aSAndroid Build Coastguard Worker case 'h':
154*61c4878aSAndroid Build Coastguard Worker human_readable_given = true;
155*61c4878aSAndroid Build Coastguard Worker break;
156*61c4878aSAndroid Build Coastguard Worker case 'i':
157*61c4878aSAndroid Build Coastguard Worker args.input_path = optarg;
158*61c4878aSAndroid Build Coastguard Worker break;
159*61c4878aSAndroid Build Coastguard Worker case 'l':
160*61c4878aSAndroid Build Coastguard Worker args.lsb_first = true;
161*61c4878aSAndroid Build Coastguard Worker break;
162*61c4878aSAndroid Build Coastguard Worker case 'm': {
163*61c4878aSAndroid Build Coastguard Worker auto mode = ParseNumber<unsigned int>(optarg);
164*61c4878aSAndroid Build Coastguard Worker if (!mode || mode.value() > 3) {
165*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Invalid mode: %s", optarg);
166*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
167*61c4878aSAndroid Build Coastguard Worker }
168*61c4878aSAndroid Build Coastguard Worker args.mode = mode.value();
169*61c4878aSAndroid Build Coastguard Worker break;
170*61c4878aSAndroid Build Coastguard Worker }
171*61c4878aSAndroid Build Coastguard Worker case 'o':
172*61c4878aSAndroid Build Coastguard Worker args.output_path = optarg;
173*61c4878aSAndroid Build Coastguard Worker break;
174*61c4878aSAndroid Build Coastguard Worker case 'r': {
175*61c4878aSAndroid Build Coastguard Worker auto count = ParseNumber<unsigned int>(optarg);
176*61c4878aSAndroid Build Coastguard Worker if (!count) {
177*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Invalid count: %s", optarg);
178*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
179*61c4878aSAndroid Build Coastguard Worker }
180*61c4878aSAndroid Build Coastguard Worker args.rx_count = count;
181*61c4878aSAndroid Build Coastguard Worker break;
182*61c4878aSAndroid Build Coastguard Worker }
183*61c4878aSAndroid Build Coastguard Worker case '?':
184*61c4878aSAndroid Build Coastguard Worker if (optopt) {
185*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Invalid flag: -%c", optopt);
186*61c4878aSAndroid Build Coastguard Worker } else {
187*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Invalid flag: %s", argv[current_optind]);
188*61c4878aSAndroid Build Coastguard Worker }
189*61c4878aSAndroid Build Coastguard Worker Usage();
190*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
191*61c4878aSAndroid Build Coastguard Worker case ':':
192*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Missing argument to %s", argv[current_optind]);
193*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
194*61c4878aSAndroid Build Coastguard Worker }
195*61c4878aSAndroid Build Coastguard Worker }
196*61c4878aSAndroid Build Coastguard Worker
197*61c4878aSAndroid Build Coastguard Worker args.human_readable = human_readable_given ||
198*61c4878aSAndroid Build Coastguard Worker (args.output_path == "-" && isatty(STDOUT_FILENO));
199*61c4878aSAndroid Build Coastguard Worker
200*61c4878aSAndroid Build Coastguard Worker // Check for required flags
201*61c4878aSAndroid Build Coastguard Worker if (args.device.empty()) {
202*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Missing required flag: -D/--device");
203*61c4878aSAndroid Build Coastguard Worker Usage();
204*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
205*61c4878aSAndroid Build Coastguard Worker }
206*61c4878aSAndroid Build Coastguard Worker if (!args.frequency) {
207*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Missing required flag: -F/--frequency");
208*61c4878aSAndroid Build Coastguard Worker Usage();
209*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
210*61c4878aSAndroid Build Coastguard Worker }
211*61c4878aSAndroid Build Coastguard Worker
212*61c4878aSAndroid Build Coastguard Worker // Either input file or rx count must be provided
213*61c4878aSAndroid Build Coastguard Worker if (!args.input_path && !args.rx_count) {
214*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Either -i/--input or -r/--rx must be provided.");
215*61c4878aSAndroid Build Coastguard Worker return Status::InvalidArgument();
216*61c4878aSAndroid Build Coastguard Worker }
217*61c4878aSAndroid Build Coastguard Worker
218*61c4878aSAndroid Build Coastguard Worker return args;
219*61c4878aSAndroid Build Coastguard Worker }
220*61c4878aSAndroid Build Coastguard Worker
ReadInput(const std::string & path,size_t limit)221*61c4878aSAndroid Build Coastguard Worker std::vector<std::byte> ReadInput(const std::string& path, size_t limit) {
222*61c4878aSAndroid Build Coastguard Worker std::ifstream input_file;
223*61c4878aSAndroid Build Coastguard Worker if (path != "-") {
224*61c4878aSAndroid Build Coastguard Worker input_file.open(path, std::ifstream::in);
225*61c4878aSAndroid Build Coastguard Worker if (!input_file.is_open()) {
226*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Failed to open %s", path.c_str());
227*61c4878aSAndroid Build Coastguard Worker exit(2);
228*61c4878aSAndroid Build Coastguard Worker }
229*61c4878aSAndroid Build Coastguard Worker }
230*61c4878aSAndroid Build Coastguard Worker std::istream& instream = input_file.is_open() ? input_file : std::cin;
231*61c4878aSAndroid Build Coastguard Worker
232*61c4878aSAndroid Build Coastguard Worker std::vector<std::byte> result;
233*61c4878aSAndroid Build Coastguard Worker for (size_t i = 0; i < limit; i++) {
234*61c4878aSAndroid Build Coastguard Worker int b = instream.get();
235*61c4878aSAndroid Build Coastguard Worker if (b == EOF) {
236*61c4878aSAndroid Build Coastguard Worker break;
237*61c4878aSAndroid Build Coastguard Worker }
238*61c4878aSAndroid Build Coastguard Worker result.push_back(static_cast<std::byte>(b));
239*61c4878aSAndroid Build Coastguard Worker }
240*61c4878aSAndroid Build Coastguard Worker
241*61c4878aSAndroid Build Coastguard Worker return result;
242*61c4878aSAndroid Build Coastguard Worker }
243*61c4878aSAndroid Build Coastguard Worker
WriteOutput(const std::string & path,std::vector<std::byte> data,bool human_readable)244*61c4878aSAndroid Build Coastguard Worker void WriteOutput(const std::string& path,
245*61c4878aSAndroid Build Coastguard Worker std::vector<std::byte> data,
246*61c4878aSAndroid Build Coastguard Worker bool human_readable) {
247*61c4878aSAndroid Build Coastguard Worker std::ofstream output_file;
248*61c4878aSAndroid Build Coastguard Worker if (path != "-") {
249*61c4878aSAndroid Build Coastguard Worker output_file.open(path, std::ifstream::out);
250*61c4878aSAndroid Build Coastguard Worker if (!output_file.is_open()) {
251*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Failed to open %s", path.c_str());
252*61c4878aSAndroid Build Coastguard Worker exit(2);
253*61c4878aSAndroid Build Coastguard Worker }
254*61c4878aSAndroid Build Coastguard Worker }
255*61c4878aSAndroid Build Coastguard Worker std::ostream& out = output_file.is_open() ? output_file : std::cout;
256*61c4878aSAndroid Build Coastguard Worker
257*61c4878aSAndroid Build Coastguard Worker if (human_readable) {
258*61c4878aSAndroid Build Coastguard Worker out << '"';
259*61c4878aSAndroid Build Coastguard Worker }
260*61c4878aSAndroid Build Coastguard Worker
261*61c4878aSAndroid Build Coastguard Worker for (std::byte b : data) {
262*61c4878aSAndroid Build Coastguard Worker char c = static_cast<char>(b);
263*61c4878aSAndroid Build Coastguard Worker if (!human_readable || std::isprint(c)) {
264*61c4878aSAndroid Build Coastguard Worker out.put(c);
265*61c4878aSAndroid Build Coastguard Worker } else if (c == '\0') {
266*61c4878aSAndroid Build Coastguard Worker out << "\\0";
267*61c4878aSAndroid Build Coastguard Worker } else if (c == '\n') {
268*61c4878aSAndroid Build Coastguard Worker out << "\\n";
269*61c4878aSAndroid Build Coastguard Worker } else {
270*61c4878aSAndroid Build Coastguard Worker out << "\\x" << std::hex << std::setfill('0') << std::setw(2)
271*61c4878aSAndroid Build Coastguard Worker << static_cast<unsigned int>(c);
272*61c4878aSAndroid Build Coastguard Worker }
273*61c4878aSAndroid Build Coastguard Worker }
274*61c4878aSAndroid Build Coastguard Worker
275*61c4878aSAndroid Build Coastguard Worker if (human_readable) {
276*61c4878aSAndroid Build Coastguard Worker out << '"' << std::endl;
277*61c4878aSAndroid Build Coastguard Worker }
278*61c4878aSAndroid Build Coastguard Worker }
279*61c4878aSAndroid Build Coastguard Worker
MainInNamespace(int argc,char * argv[])280*61c4878aSAndroid Build Coastguard Worker int MainInNamespace(int argc, char* argv[]) {
281*61c4878aSAndroid Build Coastguard Worker auto maybe_args = ParseArgs(argc, argv);
282*61c4878aSAndroid Build Coastguard Worker if (!maybe_args.ok()) {
283*61c4878aSAndroid Build Coastguard Worker return 1;
284*61c4878aSAndroid Build Coastguard Worker }
285*61c4878aSAndroid Build Coastguard Worker auto args = std::move(maybe_args.value());
286*61c4878aSAndroid Build Coastguard Worker
287*61c4878aSAndroid Build Coastguard Worker int fd = open(args.device.c_str(), O_RDWR);
288*61c4878aSAndroid Build Coastguard Worker if (fd < 0) {
289*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Failed to open %s: %s", args.device.c_str(), strerror(errno));
290*61c4878aSAndroid Build Coastguard Worker return 1;
291*61c4878aSAndroid Build Coastguard Worker }
292*61c4878aSAndroid Build Coastguard Worker PW_LOG_DEBUG("Opened %s", args.device.c_str());
293*61c4878aSAndroid Build Coastguard Worker
294*61c4878aSAndroid Build Coastguard Worker // Set up SPI Initiator.
295*61c4878aSAndroid Build Coastguard Worker LinuxInitiator initiator(fd, args.frequency);
296*61c4878aSAndroid Build Coastguard Worker if (auto status = initiator.Configure(args.GetSpiConfig()); !status.ok()) {
297*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR(
298*61c4878aSAndroid Build Coastguard Worker "Failed to configure %s: %s", args.device.c_str(), status.str());
299*61c4878aSAndroid Build Coastguard Worker return 2;
300*61c4878aSAndroid Build Coastguard Worker }
301*61c4878aSAndroid Build Coastguard Worker PW_LOG_DEBUG("Configured %s", args.device.c_str());
302*61c4878aSAndroid Build Coastguard Worker
303*61c4878aSAndroid Build Coastguard Worker // Read input data for transmit.
304*61c4878aSAndroid Build Coastguard Worker std::vector<std::byte> tx_data;
305*61c4878aSAndroid Build Coastguard Worker if (args.input_path) {
306*61c4878aSAndroid Build Coastguard Worker tx_data = ReadInput(args.input_path.value(), 1024);
307*61c4878aSAndroid Build Coastguard Worker }
308*61c4878aSAndroid Build Coastguard Worker
309*61c4878aSAndroid Build Coastguard Worker // Set up receive buffer.
310*61c4878aSAndroid Build Coastguard Worker std::vector<std::byte> rx_data(args.rx_count ? args.rx_count.value()
311*61c4878aSAndroid Build Coastguard Worker : tx_data.size());
312*61c4878aSAndroid Build Coastguard Worker
313*61c4878aSAndroid Build Coastguard Worker // Perform a transfer!
314*61c4878aSAndroid Build Coastguard Worker PW_LOG_DEBUG(
315*61c4878aSAndroid Build Coastguard Worker "Ready to send %zu, receive %zu bytes", tx_data.size(), rx_data.size());
316*61c4878aSAndroid Build Coastguard Worker if (auto status = initiator.WriteRead(tx_data, rx_data); !status.ok()) {
317*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Failed to send/recv data: %s", status.str());
318*61c4878aSAndroid Build Coastguard Worker return 2;
319*61c4878aSAndroid Build Coastguard Worker }
320*61c4878aSAndroid Build Coastguard Worker PW_LOG_DEBUG("Transfer successful! (%zu bytes)", rx_data.size());
321*61c4878aSAndroid Build Coastguard Worker
322*61c4878aSAndroid Build Coastguard Worker WriteOutput(args.output_path, rx_data, args.human_readable);
323*61c4878aSAndroid Build Coastguard Worker
324*61c4878aSAndroid Build Coastguard Worker return 0;
325*61c4878aSAndroid Build Coastguard Worker }
326*61c4878aSAndroid Build Coastguard Worker
327*61c4878aSAndroid Build Coastguard Worker } // namespace
328*61c4878aSAndroid Build Coastguard Worker } // namespace pw::spi
329*61c4878aSAndroid Build Coastguard Worker
main(int argc,char * argv[])330*61c4878aSAndroid Build Coastguard Worker int main(int argc, char* argv[]) {
331*61c4878aSAndroid Build Coastguard Worker return pw::spi::MainInNamespace(argc, argv);
332*61c4878aSAndroid Build Coastguard Worker }
333