1use std::path::Path;
30use std::time::Duration;
31
32use serde_json::{json, Value};
33
34use crate::error::{Error, Result};
35use crate::wire::{self, DebugConnection, DebugInfo, DebugStatus, Timeout};
36
37fn base64_encode(input: &[u8]) -> String {
40 const ALPHABET: &[u8; 64] =
41 b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
42 let mut out = String::with_capacity(input.len().div_ceil(3) * 4);
43 for chunk in input.chunks(3) {
44 let b0 = chunk[0] as u32;
45 let b1 = *chunk.get(1).unwrap_or(&0) as u32;
46 let b2 = *chunk.get(2).unwrap_or(&0) as u32;
47 let n = (b0 << 16) | (b1 << 8) | b2;
48 out.push(ALPHABET[(n >> 18 & 0x3F) as usize] as char);
49 out.push(ALPHABET[(n >> 12 & 0x3F) as usize] as char);
50 out.push(if chunk.len() > 1 {
51 ALPHABET[(n >> 6 & 0x3F) as usize] as char
52 } else {
53 '='
54 });
55 out.push(if chunk.len() > 2 {
56 ALPHABET[(n & 0x3F) as usize] as char
57 } else {
58 '='
59 });
60 }
61 out
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub enum FirmwareKind {
68 Hex,
70 Elf,
72 Bin,
74}
75
76impl FirmwareKind {
77 fn from_path(path: &Path) -> Result<Self> {
78 match path.extension().and_then(|e| e.to_str()).map(str::to_ascii_lowercase) {
79 Some(ext) if ext == "hex" => Ok(FirmwareKind::Hex),
80 Some(ext) if ext == "elf" => Ok(FirmwareKind::Elf),
81 Some(ext) if ext == "bin" => Ok(FirmwareKind::Bin),
82 _ => Err(Error::Config(format!(
83 "cannot infer firmware type from '{}'; use flash_with to set it explicitly",
84 path.display()
85 ))),
86 }
87 }
88}
89
90#[derive(Debug, Clone)]
92pub struct ConnectOptions {
93 pub speed: Option<String>,
96 pub force: bool,
98 pub halt: bool,
100 pub gdb: bool,
102}
103
104impl Default for ConnectOptions {
105 fn default() -> Self {
106 ConnectOptions {
107 speed: None,
108 force: false,
109 halt: false,
110 gdb: true,
111 }
112 }
113}
114
115const CONNECT_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(30));
117const FLASH_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(180));
118const ERASE_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(120));
119const RESET_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(10));
120const MEMRD_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(30));
121const QUICK_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(10));
122
123#[derive(Debug, Clone, Copy, Default)]
125pub struct RttOptions {
126 pub channel: u32,
128 pub search_addr: Option<u64>,
130 pub search_size: Option<u64>,
132}
133
134pub(crate) fn debug_body(net_record: &Value, extra: Value) -> Value {
136 let mut obj = serde_json::Map::new();
137 obj.insert("net".to_string(), net_record.clone());
138 if let Value::Object(map) = extra {
139 obj.extend(map);
140 }
141 Value::Object(obj)
142}
143
144pub(crate) mod ops {
147 use super::*;
148
149 pub(crate) fn connect(net: &Value, opts: &ConnectOptions) -> (String, Value, Timeout) {
150 let mut extra = json!({
151 "force": opts.force,
152 "halt": opts.halt,
153 "gdb": opts.gdb,
154 });
155 if let Some(speed) = &opts.speed {
156 extra["speed"] = json!(speed);
157 }
158 ("/debug/connect".into(), debug_body(net, extra), CONNECT_TIMEOUT)
159 }
160
161 pub(crate) fn disconnect(net: &Value, keep_running: bool) -> (String, Value, Timeout) {
162 (
163 "/debug/disconnect".into(),
164 debug_body(net, json!({ "keep_jlink_running": keep_running })),
165 QUICK_TIMEOUT,
166 )
167 }
168
169 pub(crate) fn reset(net: &Value, halt: bool) -> (String, Value, Timeout) {
170 (
171 "/debug/reset".into(),
172 debug_body(net, json!({ "halt": halt })),
173 RESET_TIMEOUT,
174 )
175 }
176
177 pub(crate) fn erase(net: &Value) -> (String, Value, Timeout) {
178 ("/debug/erase".into(), debug_body(net, json!({})), ERASE_TIMEOUT)
179 }
180
181 pub(crate) fn read_memory(net: &Value, address: u64, length: usize) -> (String, Value, Timeout) {
182 (
183 "/debug/memrd".into(),
184 debug_body(net, json!({ "start_addr": address, "length": length })),
185 MEMRD_TIMEOUT,
186 )
187 }
188
189 pub(crate) fn info(net: &Value) -> (String, Value, Timeout) {
190 ("/debug/info".into(), debug_body(net, json!({})), QUICK_TIMEOUT)
191 }
192
193 pub(crate) fn status(net: &Value) -> (String, Value, Timeout) {
194 ("/debug/status".into(), debug_body(net, json!({})), QUICK_TIMEOUT)
195 }
196
197 pub(crate) fn flash(
200 net: &Value,
201 contents: &[u8],
202 kind: FirmwareKind,
203 address: Option<u32>,
204 ) -> (String, Value, Timeout) {
205 let b64 = base64_encode(contents);
206 let payload = match kind {
207 FirmwareKind::Hex => json!({ "hexfile": { "content": b64 } }),
208 FirmwareKind::Elf => json!({ "elffile": { "content": b64 } }),
209 FirmwareKind::Bin => json!({
210 "binfile": { "content": b64, "address": address.unwrap_or(0x0800_0000) }
211 }),
212 };
213 ("/debug/flash".into(), debug_body(net, payload), FLASH_TIMEOUT)
214 }
215
216 #[cfg(feature = "blocking")]
219 pub(crate) fn rtt_body(net: &Value, opts: &RttOptions) -> Value {
220 let mut extra = json!({ "channel": opts.channel, "timeout": Value::Null });
221 if let Some(a) = opts.search_addr {
222 extra["search_addr"] = json!(a);
223 }
224 if let Some(s) = opts.search_size {
225 extra["search_size"] = json!(s);
226 }
227 debug_body(net, extra)
228 }
229}
230
231pub(crate) fn find_debug_record(records: Vec<Value>, name: &str) -> Result<Value> {
233 let mut wrong_role = false;
234 for rec in records {
235 if rec.get("name").and_then(Value::as_str) == Some(name) {
236 if rec.get("role").and_then(Value::as_str) == Some("debug") {
237 return Ok(rec);
238 }
239 wrong_role = true;
240 }
241 }
242 Err(Error::Box {
243 status: 404,
244 message: if wrong_role {
245 format!("net '{name}' exists but is not a debug net")
246 } else {
247 format!("debug net '{name}' not found on this box")
248 },
249 })
250}
251
252pub(crate) fn read_firmware(path: &Path) -> Result<(Vec<u8>, FirmwareKind)> {
254 let kind = FirmwareKind::from_path(path)?;
255 let bytes = std::fs::read(path)
256 .map_err(|e| Error::Config(format!("cannot read firmware '{}': {e}", path.display())))?;
257 Ok((bytes, kind))
258}
259
260#[cfg(feature = "blocking")]
269#[derive(Clone)]
270pub struct DebugNet<'a> {
271 pub(crate) client: &'a crate::client::LagerBox,
272 pub(crate) name: String,
273}
274
275#[cfg(feature = "blocking")]
276impl DebugNet<'_> {
277 pub fn name(&self) -> &str {
279 &self.name
280 }
281
282 fn net_record(&self) -> Result<Value> {
283 self.client.debug_net_record(&self.name)
284 }
285
286 fn call(&self, path: &str, body: Value, timeout: Timeout) -> Result<Value> {
287 let req = wire::debug_request(path, body, timeout);
288 let (status, resp) = self.client.execute_debug(&req)?;
289 wire::parse_debug(status, resp)
290 }
291
292 pub fn connect(&self) -> Result<DebugConnection> {
294 self.connect_with(&ConnectOptions::default())
295 }
296
297 pub fn connect_with(&self, opts: &ConnectOptions) -> Result<DebugConnection> {
299 let net = self.net_record()?;
300 let (path, body, timeout) = ops::connect(&net, opts);
301 let resp = self.call(&path, body, timeout)?;
302 serde_json::from_value(resp).map_err(Into::into)
303 }
304
305 pub fn disconnect(&self, keep_running: bool) -> Result<()> {
308 let net = self.net_record()?;
309 let (path, body, timeout) = ops::disconnect(&net, keep_running);
310 self.call(&path, body, timeout).map(|_| ())
311 }
312
313 pub fn reset(&self, halt: bool) -> Result<()> {
315 let net = self.net_record()?;
316 let (path, body, timeout) = ops::reset(&net, halt);
317 self.call(&path, body, timeout).map(|_| ())
318 }
319
320 pub fn erase(&self) -> Result<()> {
322 let net = self.net_record()?;
323 let (path, body, timeout) = ops::erase(&net);
324 self.call(&path, body, timeout).map(|_| ())
325 }
326
327 pub fn flash(&self, firmware_path: impl AsRef<Path>) -> Result<()> {
331 let path = firmware_path.as_ref();
332 let (contents, kind) = read_firmware(path)?;
333 self.flash_bytes(&contents, kind, None)
334 }
335
336 pub fn flash_bin(&self, firmware_path: impl AsRef<Path>, address: u32) -> Result<()> {
338 let contents = std::fs::read(firmware_path.as_ref())
339 .map_err(|e| Error::Config(format!("cannot read firmware: {e}")))?;
340 self.flash_bytes(&contents, FirmwareKind::Bin, Some(address))
341 }
342
343 pub fn flash_bytes(
345 &self,
346 contents: &[u8],
347 kind: FirmwareKind,
348 address: Option<u32>,
349 ) -> Result<()> {
350 let net = self.net_record()?;
351 let (path, body, timeout) = ops::flash(&net, contents, kind, address);
352 self.call(&path, body, timeout).map(|_| ())
353 }
354
355 pub fn read_memory(&self, address: u64, length: usize) -> Result<Vec<u8>> {
357 let net = self.net_record()?;
358 let (path, body, timeout) = ops::read_memory(&net, address, length);
359 let resp = self.call(&path, body, timeout)?;
360 wire::debug_memory_bytes(&resp)
361 }
362
363 pub fn info(&self) -> Result<DebugInfo> {
365 let net = self.net_record()?;
366 let (path, body, timeout) = ops::info(&net);
367 let resp = self.call(&path, body, timeout)?;
368 serde_json::from_value(resp).map_err(Into::into)
369 }
370
371 pub fn status(&self) -> Result<DebugStatus> {
373 let net = self.net_record()?;
374 let (path, body, timeout) = ops::status(&net);
375 let resp = self.call(&path, body, timeout)?;
376 serde_json::from_value(resp).map_err(Into::into)
377 }
378
379 pub fn rtt(&self) -> Result<RttStream> {
385 self.rtt_with(&RttOptions::default())
386 }
387
388 pub fn rtt_with(&self, opts: &RttOptions) -> Result<RttStream> {
390 let net = self.net_record()?;
391 let body = ops::rtt_body(&net, opts);
392 let req = wire::debug_request("/debug/rtt", body, Timeout::Unbounded);
393 let reader = self.client.stream_debug(&req)?;
394 Ok(RttStream { reader })
395 }
396}
397
398#[cfg(feature = "blocking")]
401pub struct RttStream {
402 reader: Box<dyn std::io::Read + Send + Sync>,
403}
404
405#[cfg(feature = "blocking")]
406impl std::io::Read for RttStream {
407 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
408 self.reader.read(buf)
409 }
410}
411
412#[cfg(feature = "async")]
421#[derive(Clone)]
422pub struct AsyncDebugNet<'a> {
423 pub(crate) client: &'a crate::async_client::AsyncLagerBox,
424 pub(crate) name: String,
425}
426
427#[cfg(feature = "async")]
428impl AsyncDebugNet<'_> {
429 pub fn name(&self) -> &str {
431 &self.name
432 }
433
434 async fn net_record(&self) -> Result<Value> {
435 self.client.debug_net_record(&self.name).await
436 }
437
438 async fn call(&self, path: &str, body: Value, timeout: Timeout) -> Result<Value> {
439 let req = wire::debug_request(path, body, timeout);
440 let (status, resp) = self.client.execute_debug(&req).await?;
441 wire::parse_debug(status, resp)
442 }
443
444 pub async fn connect(&self) -> Result<DebugConnection> {
446 self.connect_with(&ConnectOptions::default()).await
447 }
448
449 pub async fn connect_with(&self, opts: &ConnectOptions) -> Result<DebugConnection> {
451 let net = self.net_record().await?;
452 let (path, body, timeout) = ops::connect(&net, opts);
453 let resp = self.call(&path, body, timeout).await?;
454 serde_json::from_value(resp).map_err(Into::into)
455 }
456
457 pub async fn disconnect(&self, keep_running: bool) -> Result<()> {
459 let net = self.net_record().await?;
460 let (path, body, timeout) = ops::disconnect(&net, keep_running);
461 self.call(&path, body, timeout).await.map(|_| ())
462 }
463
464 pub async fn reset(&self, halt: bool) -> Result<()> {
466 let net = self.net_record().await?;
467 let (path, body, timeout) = ops::reset(&net, halt);
468 self.call(&path, body, timeout).await.map(|_| ())
469 }
470
471 pub async fn erase(&self) -> Result<()> {
473 let net = self.net_record().await?;
474 let (path, body, timeout) = ops::erase(&net);
475 self.call(&path, body, timeout).await.map(|_| ())
476 }
477
478 pub async fn flash(&self, firmware_path: impl AsRef<Path>) -> Result<()> {
480 let (contents, kind) = read_firmware(firmware_path.as_ref())?;
481 self.flash_bytes(&contents, kind, None).await
482 }
483
484 pub async fn flash_bin(&self, firmware_path: impl AsRef<Path>, address: u32) -> Result<()> {
486 let contents = std::fs::read(firmware_path.as_ref())
487 .map_err(|e| Error::Config(format!("cannot read firmware: {e}")))?;
488 self.flash_bytes(&contents, FirmwareKind::Bin, Some(address)).await
489 }
490
491 pub async fn flash_bytes(
493 &self,
494 contents: &[u8],
495 kind: FirmwareKind,
496 address: Option<u32>,
497 ) -> Result<()> {
498 let net = self.net_record().await?;
499 let (path, body, timeout) = ops::flash(&net, contents, kind, address);
500 self.call(&path, body, timeout).await.map(|_| ())
501 }
502
503 pub async fn read_memory(&self, address: u64, length: usize) -> Result<Vec<u8>> {
505 let net = self.net_record().await?;
506 let (path, body, timeout) = ops::read_memory(&net, address, length);
507 let resp = self.call(&path, body, timeout).await?;
508 wire::debug_memory_bytes(&resp)
509 }
510
511 pub async fn info(&self) -> Result<DebugInfo> {
513 let net = self.net_record().await?;
514 let (path, body, timeout) = ops::info(&net);
515 let resp = self.call(&path, body, timeout).await?;
516 serde_json::from_value(resp).map_err(Into::into)
517 }
518
519 pub async fn status(&self) -> Result<DebugStatus> {
521 let net = self.net_record().await?;
522 let (path, body, timeout) = ops::status(&net);
523 let resp = self.call(&path, body, timeout).await?;
524 serde_json::from_value(resp).map_err(Into::into)
525 }
526}
527
528#[cfg(test)]
529mod tests {
530 use super::*;
531
532 #[test]
533 fn base64_matches_reference() {
534 assert_eq!(base64_encode(b""), "");
535 assert_eq!(base64_encode(b"f"), "Zg==");
536 assert_eq!(base64_encode(b"fo"), "Zm8=");
537 assert_eq!(base64_encode(b"foo"), "Zm9v");
538 assert_eq!(base64_encode(b"foob"), "Zm9vYg==");
539 assert_eq!(base64_encode(b"fooba"), "Zm9vYmE=");
540 assert_eq!(base64_encode(b"foobar"), "Zm9vYmFy");
541 assert_eq!(base64_encode(&[0x00, 0xff, 0x10]), "AP8Q");
542 }
543
544 #[test]
545 fn firmware_kind_from_extension() {
546 assert_eq!(
547 FirmwareKind::from_path(Path::new("a/b/fw.hex")).unwrap(),
548 FirmwareKind::Hex
549 );
550 assert_eq!(
551 FirmwareKind::from_path(Path::new("FW.ELF")).unwrap(),
552 FirmwareKind::Elf
553 );
554 assert!(FirmwareKind::from_path(Path::new("fw.txt")).is_err());
555 }
556
557 #[test]
558 fn debug_body_wraps_net_and_params() {
559 let net = json!({"name": "debug1", "role": "debug", "pin": "nrf52"});
560 let body = debug_body(&net, json!({"halt": true}));
561 assert_eq!(body["net"]["name"], "debug1");
562 assert_eq!(body["halt"], true);
563 }
564}