1use std::path::Path;
30use std::sync::{Arc, Mutex};
31use std::time::Duration;
32
33use serde_json::{json, Value};
34
35use crate::error::{Error, Result};
36use crate::wire::{self, DebugConnection, DebugInfo, DebugStatus, Timeout};
37
38pub(crate) fn base64_encode(input: &[u8]) -> String {
42 const ALPHABET: &[u8; 64] =
43 b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
44 let mut out = String::with_capacity(input.len().div_ceil(3) * 4);
45 for chunk in input.chunks(3) {
46 let b0 = chunk[0] as u32;
47 let b1 = *chunk.get(1).unwrap_or(&0) as u32;
48 let b2 = *chunk.get(2).unwrap_or(&0) as u32;
49 let n = (b0 << 16) | (b1 << 8) | b2;
50 out.push(ALPHABET[(n >> 18 & 0x3F) as usize] as char);
51 out.push(ALPHABET[(n >> 12 & 0x3F) as usize] as char);
52 out.push(if chunk.len() > 1 {
53 ALPHABET[(n >> 6 & 0x3F) as usize] as char
54 } else {
55 '='
56 });
57 out.push(if chunk.len() > 2 {
58 ALPHABET[(n & 0x3F) as usize] as char
59 } else {
60 '='
61 });
62 }
63 out
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub enum FirmwareKind {
70 Hex,
72 Elf,
74 Bin,
76}
77
78impl FirmwareKind {
79 fn from_path(path: &Path) -> Result<Self> {
80 match path.extension().and_then(|e| e.to_str()).map(str::to_ascii_lowercase) {
81 Some(ext) if ext == "hex" => Ok(FirmwareKind::Hex),
82 Some(ext) if ext == "elf" => Ok(FirmwareKind::Elf),
83 Some(ext) if ext == "bin" => Ok(FirmwareKind::Bin),
84 _ => Err(Error::Config(format!(
85 "cannot infer firmware type from '{}'; use flash_with to set it explicitly",
86 path.display()
87 ))),
88 }
89 }
90}
91
92#[derive(Debug, Clone)]
94pub struct ConnectOptions {
95 pub speed: Option<String>,
98 pub force: bool,
100 pub halt: bool,
110 pub gdb: bool,
112 pub jlink_script: Option<Vec<u8>>,
116 pub openocd_config: Option<Vec<u8>>,
123}
124
125impl Default for ConnectOptions {
126 fn default() -> Self {
127 ConnectOptions {
128 speed: None,
129 force: false,
130 halt: false,
131 gdb: true,
132 jlink_script: None,
133 openocd_config: None,
134 }
135 }
136}
137
138const CONNECT_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(30));
140const FLASH_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(180));
141const ERASE_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(120));
142const RESET_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(10));
143const MEMRD_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(30));
144const QUICK_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(10));
145
146#[derive(Debug, Clone, Copy, Default)]
148pub struct RttOptions {
149 pub channel: u32,
151 pub search_addr: Option<u64>,
153 pub search_size: Option<u64>,
155 pub chunk_size: Option<u64>,
158}
159
160pub(crate) fn debug_body(net_record: &Value, extra: Value) -> Value {
162 let mut obj = serde_json::Map::new();
163 obj.insert("net".to_string(), net_record.clone());
164 if let Value::Object(map) = extra {
165 obj.extend(map);
166 }
167 Value::Object(obj)
168}
169
170pub(crate) mod ops {
173 use super::*;
174
175 pub(crate) fn connect(net: &Value, opts: &ConnectOptions) -> (String, Value, Timeout) {
176 let mut extra = json!({
177 "force": opts.force,
178 "halt": opts.halt,
179 "gdb": opts.gdb,
180 });
181 if let Some(speed) = &opts.speed {
182 extra["speed"] = json!(speed);
183 }
184 if let Some(script) = &opts.jlink_script {
185 extra["jlink_script"] = json!(base64_encode(script));
186 }
187 if let Some(cfg) = &opts.openocd_config {
188 extra["openocd_config"] = json!(base64_encode(cfg));
189 }
190 ("/debug/connect".into(), debug_body(net, extra), CONNECT_TIMEOUT)
191 }
192
193 pub(crate) fn disconnect(net: &Value, keep_running: bool) -> (String, Value, Timeout) {
194 (
195 "/debug/disconnect".into(),
196 debug_body(net, json!({ "keep_jlink_running": keep_running })),
197 QUICK_TIMEOUT,
198 )
199 }
200
201 pub(crate) fn reset(net: &Value, halt: bool) -> (String, Value, Timeout) {
202 (
203 "/debug/reset".into(),
204 debug_body(net, json!({ "halt": halt })),
205 RESET_TIMEOUT,
206 )
207 }
208
209 pub(crate) fn erase(net: &Value) -> (String, Value, Timeout) {
210 ("/debug/erase".into(), debug_body(net, json!({})), ERASE_TIMEOUT)
211 }
212
213 pub(crate) fn read_memory(net: &Value, address: u64, length: usize) -> (String, Value, Timeout) {
214 (
215 "/debug/memrd".into(),
216 debug_body(net, json!({ "start_addr": address, "length": length })),
217 MEMRD_TIMEOUT,
218 )
219 }
220
221 pub(crate) fn info(net: &Value) -> (String, Value, Timeout) {
222 ("/debug/info".into(), debug_body(net, json!({})), QUICK_TIMEOUT)
223 }
224
225 pub(crate) fn status(net: &Value) -> (String, Value, Timeout) {
226 ("/debug/status".into(), debug_body(net, json!({})), QUICK_TIMEOUT)
227 }
228
229 pub(crate) fn flash(
232 net: &Value,
233 contents: &[u8],
234 kind: FirmwareKind,
235 address: Option<u32>,
236 ) -> (String, Value, Timeout) {
237 let b64 = base64_encode(contents);
238 let payload = match kind {
239 FirmwareKind::Hex => json!({ "hexfile": { "content": b64 } }),
240 FirmwareKind::Elf => json!({ "elffile": { "content": b64 } }),
241 FirmwareKind::Bin => json!({
242 "binfile": { "content": b64, "address": address.unwrap_or(0x0800_0000) }
243 }),
244 };
245 ("/debug/flash".into(), debug_body(net, payload), FLASH_TIMEOUT)
246 }
247
248 #[cfg(feature = "blocking")]
251 pub(crate) fn rtt_body(net: &Value, opts: &RttOptions) -> Value {
252 let mut extra = json!({ "channel": opts.channel, "timeout": Value::Null });
253 if let Some(a) = opts.search_addr {
254 extra["search_addr"] = json!(a);
255 }
256 if let Some(s) = opts.search_size {
257 extra["search_size"] = json!(s);
258 }
259 debug_body(net, extra)
260 }
261}
262
263pub(crate) fn find_debug_record(records: Vec<Value>, name: &str) -> Result<Value> {
265 let mut wrong_role = false;
266 for rec in records {
267 if rec.get("name").and_then(Value::as_str) == Some(name) {
268 if rec.get("role").and_then(Value::as_str) == Some("debug") {
269 return Ok(rec);
270 }
271 wrong_role = true;
272 }
273 }
274 Err(Error::Box {
275 status: 404,
276 message: if wrong_role {
277 format!("net '{name}' exists but is not a debug net")
278 } else {
279 format!("debug net '{name}' not found on this box")
280 },
281 })
282}
283
284pub(crate) fn read_firmware(path: &Path) -> Result<(Vec<u8>, FirmwareKind)> {
286 let kind = FirmwareKind::from_path(path)?;
287 let bytes = std::fs::read(path)
288 .map_err(|e| Error::Config(format!("cannot read firmware '{}': {e}", path.display())))?;
289 Ok((bytes, kind))
290}
291
292#[cfg(feature = "blocking")]
304#[derive(Clone)]
305pub struct DebugNet<'a> {
306 pub(crate) client: &'a crate::client::LagerBox,
307 pub(crate) name: String,
308 pub(crate) record: Arc<Mutex<Option<Value>>>,
309}
310
311#[cfg(feature = "blocking")]
312impl DebugNet<'_> {
313 pub fn name(&self) -> &str {
315 &self.name
316 }
317
318 fn net_record(&self) -> Result<Value> {
319 if let Some(rec) = self.record.lock().unwrap().clone() {
320 return Ok(rec);
321 }
322 let rec = self.client.debug_net_record(&self.name)?;
323 *self.record.lock().unwrap() = Some(rec.clone());
324 Ok(rec)
325 }
326
327 fn call(&self, path: &str, body: Value, timeout: Timeout) -> Result<Value> {
328 let req = wire::debug_request(path, body, timeout);
329 let result = self
330 .client
331 .execute_debug(&req)
332 .and_then(|(status, resp)| wire::parse_debug(status, resp));
333 if result.is_err() {
334 *self.record.lock().unwrap() = None;
337 }
338 result
339 }
340
341 pub fn connect(&self) -> Result<DebugConnection> {
343 self.connect_with(&ConnectOptions::default())
344 }
345
346 pub fn connect_with(&self, opts: &ConnectOptions) -> Result<DebugConnection> {
348 let net = self.net_record()?;
349 let (path, body, timeout) = ops::connect(&net, opts);
350 let resp = self.call(&path, body, timeout)?;
351 serde_json::from_value(resp).map_err(Into::into)
352 }
353
354 pub fn disconnect(&self, keep_running: bool) -> Result<()> {
357 let net = self.net_record()?;
358 let (path, body, timeout) = ops::disconnect(&net, keep_running);
359 self.call(&path, body, timeout).map(|_| ())
360 }
361
362 pub fn reset(&self, halt: bool) -> Result<()> {
364 let net = self.net_record()?;
365 let (path, body, timeout) = ops::reset(&net, halt);
366 self.call(&path, body, timeout).map(|_| ())
367 }
368
369 pub fn erase(&self) -> Result<()> {
371 let net = self.net_record()?;
372 let (path, body, timeout) = ops::erase(&net);
373 self.call(&path, body, timeout).map(|_| ())
374 }
375
376 pub fn flash(&self, firmware_path: impl AsRef<Path>) -> Result<()> {
380 let path = firmware_path.as_ref();
381 let (contents, kind) = read_firmware(path)?;
382 self.flash_bytes(&contents, kind, None)
383 }
384
385 pub fn flash_bin(&self, firmware_path: impl AsRef<Path>, address: u32) -> Result<()> {
387 let contents = std::fs::read(firmware_path.as_ref())
388 .map_err(|e| Error::Config(format!("cannot read firmware: {e}")))?;
389 self.flash_bytes(&contents, FirmwareKind::Bin, Some(address))
390 }
391
392 pub fn flash_bytes(
394 &self,
395 contents: &[u8],
396 kind: FirmwareKind,
397 address: Option<u32>,
398 ) -> Result<()> {
399 let net = self.net_record()?;
400 let (path, body, timeout) = ops::flash(&net, contents, kind, address);
401 self.call(&path, body, timeout).map(|_| ())
402 }
403
404 pub fn read_memory(&self, address: u64, length: usize) -> Result<Vec<u8>> {
406 let net = self.net_record()?;
407 let (path, body, timeout) = ops::read_memory(&net, address, length);
408 let resp = self.call(&path, body, timeout)?;
409 wire::debug_memory_bytes(&resp)
410 }
411
412 pub fn info(&self) -> Result<DebugInfo> {
414 let net = self.net_record()?;
415 let (path, body, timeout) = ops::info(&net);
416 let resp = self.call(&path, body, timeout)?;
417 serde_json::from_value(resp).map_err(Into::into)
418 }
419
420 pub fn status(&self) -> Result<DebugStatus> {
422 let net = self.net_record()?;
423 let (path, body, timeout) = ops::status(&net);
424 let resp = self.call(&path, body, timeout)?;
425 serde_json::from_value(resp).map_err(Into::into)
426 }
427
428 pub fn rtt(&self) -> Result<RttStream> {
434 self.rtt_with(&RttOptions::default())
435 }
436
437 pub fn rtt_with(&self, opts: &RttOptions) -> Result<RttStream> {
439 let net = self.net_record()?;
440 let body = ops::rtt_body(&net, opts);
441 let req = wire::debug_request("/debug/rtt", body, Timeout::Unbounded);
442 let reader = self.client.stream_debug(&req)?;
443 Ok(RttStream { reader })
444 }
445
446 #[cfg(feature = "rtt")]
477 pub fn rtt_interactive(&self) -> Result<crate::nets::rtt::RttSession> {
478 self.rtt_interactive_with(&RttOptions::default())
479 }
480
481 #[cfg(feature = "rtt")]
485 pub fn rtt_interactive_with(&self, opts: &RttOptions) -> Result<crate::nets::rtt::RttSession> {
486 crate::nets::rtt::RttSession::open(
487 self.client.base_url(),
488 self.name.clone(),
489 opts,
490 self.client.current_token(),
491 )
492 }
493}
494
495#[cfg(feature = "blocking")]
498pub struct RttStream {
499 reader: Box<dyn std::io::Read + Send + Sync>,
500}
501
502#[cfg(feature = "blocking")]
503impl std::io::Read for RttStream {
504 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
505 self.reader.read(buf)
506 }
507}
508
509#[cfg(feature = "async")]
521#[derive(Clone)]
522pub struct AsyncDebugNet<'a> {
523 pub(crate) client: &'a crate::async_client::AsyncLagerBox,
524 pub(crate) name: String,
525 pub(crate) record: Arc<Mutex<Option<Value>>>,
526}
527
528#[cfg(feature = "async")]
529impl AsyncDebugNet<'_> {
530 pub fn name(&self) -> &str {
532 &self.name
533 }
534
535 async fn net_record(&self) -> Result<Value> {
536 if let Some(rec) = self.record.lock().unwrap().clone() {
537 return Ok(rec);
538 }
539 let rec = self.client.debug_net_record(&self.name).await?;
540 *self.record.lock().unwrap() = Some(rec.clone());
541 Ok(rec)
542 }
543
544 async fn call(&self, path: &str, body: Value, timeout: Timeout) -> Result<Value> {
545 let req = wire::debug_request(path, body, timeout);
546 let result = match self.client.execute_debug(&req).await {
547 Ok((status, resp)) => wire::parse_debug(status, resp),
548 Err(e) => Err(e),
549 };
550 if result.is_err() {
551 *self.record.lock().unwrap() = None;
554 }
555 result
556 }
557
558 pub async fn connect(&self) -> Result<DebugConnection> {
560 self.connect_with(&ConnectOptions::default()).await
561 }
562
563 pub async fn connect_with(&self, opts: &ConnectOptions) -> Result<DebugConnection> {
565 let net = self.net_record().await?;
566 let (path, body, timeout) = ops::connect(&net, opts);
567 let resp = self.call(&path, body, timeout).await?;
568 serde_json::from_value(resp).map_err(Into::into)
569 }
570
571 pub async fn disconnect(&self, keep_running: bool) -> Result<()> {
573 let net = self.net_record().await?;
574 let (path, body, timeout) = ops::disconnect(&net, keep_running);
575 self.call(&path, body, timeout).await.map(|_| ())
576 }
577
578 pub async fn reset(&self, halt: bool) -> Result<()> {
580 let net = self.net_record().await?;
581 let (path, body, timeout) = ops::reset(&net, halt);
582 self.call(&path, body, timeout).await.map(|_| ())
583 }
584
585 pub async fn erase(&self) -> Result<()> {
587 let net = self.net_record().await?;
588 let (path, body, timeout) = ops::erase(&net);
589 self.call(&path, body, timeout).await.map(|_| ())
590 }
591
592 pub async fn flash(&self, firmware_path: impl AsRef<Path>) -> Result<()> {
594 let (contents, kind) = read_firmware(firmware_path.as_ref())?;
595 self.flash_bytes(&contents, kind, None).await
596 }
597
598 pub async fn flash_bin(&self, firmware_path: impl AsRef<Path>, address: u32) -> Result<()> {
600 let contents = std::fs::read(firmware_path.as_ref())
601 .map_err(|e| Error::Config(format!("cannot read firmware: {e}")))?;
602 self.flash_bytes(&contents, FirmwareKind::Bin, Some(address)).await
603 }
604
605 pub async fn flash_bytes(
607 &self,
608 contents: &[u8],
609 kind: FirmwareKind,
610 address: Option<u32>,
611 ) -> Result<()> {
612 let net = self.net_record().await?;
613 let (path, body, timeout) = ops::flash(&net, contents, kind, address);
614 self.call(&path, body, timeout).await.map(|_| ())
615 }
616
617 pub async fn read_memory(&self, address: u64, length: usize) -> Result<Vec<u8>> {
619 let net = self.net_record().await?;
620 let (path, body, timeout) = ops::read_memory(&net, address, length);
621 let resp = self.call(&path, body, timeout).await?;
622 wire::debug_memory_bytes(&resp)
623 }
624
625 pub async fn info(&self) -> Result<DebugInfo> {
627 let net = self.net_record().await?;
628 let (path, body, timeout) = ops::info(&net);
629 let resp = self.call(&path, body, timeout).await?;
630 serde_json::from_value(resp).map_err(Into::into)
631 }
632
633 pub async fn status(&self) -> Result<DebugStatus> {
635 let net = self.net_record().await?;
636 let (path, body, timeout) = ops::status(&net);
637 let resp = self.call(&path, body, timeout).await?;
638 serde_json::from_value(resp).map_err(Into::into)
639 }
640}
641
642#[cfg(test)]
643mod tests {
644 use super::*;
645
646 #[test]
647 fn base64_matches_reference() {
648 assert_eq!(base64_encode(b""), "");
649 assert_eq!(base64_encode(b"f"), "Zg==");
650 assert_eq!(base64_encode(b"fo"), "Zm8=");
651 assert_eq!(base64_encode(b"foo"), "Zm9v");
652 assert_eq!(base64_encode(b"foob"), "Zm9vYg==");
653 assert_eq!(base64_encode(b"fooba"), "Zm9vYmE=");
654 assert_eq!(base64_encode(b"foobar"), "Zm9vYmFy");
655 assert_eq!(base64_encode(&[0x00, 0xff, 0x10]), "AP8Q");
656 }
657
658 #[test]
659 fn firmware_kind_from_extension() {
660 assert_eq!(
661 FirmwareKind::from_path(Path::new("a/b/fw.hex")).unwrap(),
662 FirmwareKind::Hex
663 );
664 assert_eq!(
665 FirmwareKind::from_path(Path::new("FW.ELF")).unwrap(),
666 FirmwareKind::Elf
667 );
668 assert!(FirmwareKind::from_path(Path::new("fw.txt")).is_err());
669 }
670
671 #[test]
672 fn debug_body_wraps_net_and_params() {
673 let net = json!({"name": "debug1", "role": "debug", "pin": "nrf52"});
674 let body = debug_body(&net, json!({"halt": true}));
675 assert_eq!(body["net"]["name"], "debug1");
676 assert_eq!(body["halt"], true);
677 }
678}