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,
102 pub gdb: bool,
104}
105
106impl Default for ConnectOptions {
107 fn default() -> Self {
108 ConnectOptions {
109 speed: None,
110 force: false,
111 halt: false,
112 gdb: true,
113 }
114 }
115}
116
117const CONNECT_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(30));
119const FLASH_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(180));
120const ERASE_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(120));
121const RESET_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(10));
122const MEMRD_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(30));
123const QUICK_TIMEOUT: Timeout = Timeout::After(Duration::from_secs(10));
124
125#[derive(Debug, Clone, Copy, Default)]
127pub struct RttOptions {
128 pub channel: u32,
130 pub search_addr: Option<u64>,
132 pub search_size: Option<u64>,
134 pub chunk_size: Option<u64>,
137}
138
139pub(crate) fn debug_body(net_record: &Value, extra: Value) -> Value {
141 let mut obj = serde_json::Map::new();
142 obj.insert("net".to_string(), net_record.clone());
143 if let Value::Object(map) = extra {
144 obj.extend(map);
145 }
146 Value::Object(obj)
147}
148
149pub(crate) mod ops {
152 use super::*;
153
154 pub(crate) fn connect(net: &Value, opts: &ConnectOptions) -> (String, Value, Timeout) {
155 let mut extra = json!({
156 "force": opts.force,
157 "halt": opts.halt,
158 "gdb": opts.gdb,
159 });
160 if let Some(speed) = &opts.speed {
161 extra["speed"] = json!(speed);
162 }
163 ("/debug/connect".into(), debug_body(net, extra), CONNECT_TIMEOUT)
164 }
165
166 pub(crate) fn disconnect(net: &Value, keep_running: bool) -> (String, Value, Timeout) {
167 (
168 "/debug/disconnect".into(),
169 debug_body(net, json!({ "keep_jlink_running": keep_running })),
170 QUICK_TIMEOUT,
171 )
172 }
173
174 pub(crate) fn reset(net: &Value, halt: bool) -> (String, Value, Timeout) {
175 (
176 "/debug/reset".into(),
177 debug_body(net, json!({ "halt": halt })),
178 RESET_TIMEOUT,
179 )
180 }
181
182 pub(crate) fn erase(net: &Value) -> (String, Value, Timeout) {
183 ("/debug/erase".into(), debug_body(net, json!({})), ERASE_TIMEOUT)
184 }
185
186 pub(crate) fn read_memory(net: &Value, address: u64, length: usize) -> (String, Value, Timeout) {
187 (
188 "/debug/memrd".into(),
189 debug_body(net, json!({ "start_addr": address, "length": length })),
190 MEMRD_TIMEOUT,
191 )
192 }
193
194 pub(crate) fn info(net: &Value) -> (String, Value, Timeout) {
195 ("/debug/info".into(), debug_body(net, json!({})), QUICK_TIMEOUT)
196 }
197
198 pub(crate) fn status(net: &Value) -> (String, Value, Timeout) {
199 ("/debug/status".into(), debug_body(net, json!({})), QUICK_TIMEOUT)
200 }
201
202 pub(crate) fn flash(
205 net: &Value,
206 contents: &[u8],
207 kind: FirmwareKind,
208 address: Option<u32>,
209 ) -> (String, Value, Timeout) {
210 let b64 = base64_encode(contents);
211 let payload = match kind {
212 FirmwareKind::Hex => json!({ "hexfile": { "content": b64 } }),
213 FirmwareKind::Elf => json!({ "elffile": { "content": b64 } }),
214 FirmwareKind::Bin => json!({
215 "binfile": { "content": b64, "address": address.unwrap_or(0x0800_0000) }
216 }),
217 };
218 ("/debug/flash".into(), debug_body(net, payload), FLASH_TIMEOUT)
219 }
220
221 #[cfg(feature = "blocking")]
224 pub(crate) fn rtt_body(net: &Value, opts: &RttOptions) -> Value {
225 let mut extra = json!({ "channel": opts.channel, "timeout": Value::Null });
226 if let Some(a) = opts.search_addr {
227 extra["search_addr"] = json!(a);
228 }
229 if let Some(s) = opts.search_size {
230 extra["search_size"] = json!(s);
231 }
232 debug_body(net, extra)
233 }
234}
235
236pub(crate) fn find_debug_record(records: Vec<Value>, name: &str) -> Result<Value> {
238 let mut wrong_role = false;
239 for rec in records {
240 if rec.get("name").and_then(Value::as_str) == Some(name) {
241 if rec.get("role").and_then(Value::as_str) == Some("debug") {
242 return Ok(rec);
243 }
244 wrong_role = true;
245 }
246 }
247 Err(Error::Box {
248 status: 404,
249 message: if wrong_role {
250 format!("net '{name}' exists but is not a debug net")
251 } else {
252 format!("debug net '{name}' not found on this box")
253 },
254 })
255}
256
257pub(crate) fn read_firmware(path: &Path) -> Result<(Vec<u8>, FirmwareKind)> {
259 let kind = FirmwareKind::from_path(path)?;
260 let bytes = std::fs::read(path)
261 .map_err(|e| Error::Config(format!("cannot read firmware '{}': {e}", path.display())))?;
262 Ok((bytes, kind))
263}
264
265#[cfg(feature = "blocking")]
277#[derive(Clone)]
278pub struct DebugNet<'a> {
279 pub(crate) client: &'a crate::client::LagerBox,
280 pub(crate) name: String,
281 pub(crate) record: Arc<Mutex<Option<Value>>>,
282}
283
284#[cfg(feature = "blocking")]
285impl DebugNet<'_> {
286 pub fn name(&self) -> &str {
288 &self.name
289 }
290
291 fn net_record(&self) -> Result<Value> {
292 if let Some(rec) = self.record.lock().unwrap().clone() {
293 return Ok(rec);
294 }
295 let rec = self.client.debug_net_record(&self.name)?;
296 *self.record.lock().unwrap() = Some(rec.clone());
297 Ok(rec)
298 }
299
300 fn call(&self, path: &str, body: Value, timeout: Timeout) -> Result<Value> {
301 let req = wire::debug_request(path, body, timeout);
302 let result = self
303 .client
304 .execute_debug(&req)
305 .and_then(|(status, resp)| wire::parse_debug(status, resp));
306 if result.is_err() {
307 *self.record.lock().unwrap() = None;
310 }
311 result
312 }
313
314 pub fn connect(&self) -> Result<DebugConnection> {
316 self.connect_with(&ConnectOptions::default())
317 }
318
319 pub fn connect_with(&self, opts: &ConnectOptions) -> Result<DebugConnection> {
321 let net = self.net_record()?;
322 let (path, body, timeout) = ops::connect(&net, opts);
323 let resp = self.call(&path, body, timeout)?;
324 serde_json::from_value(resp).map_err(Into::into)
325 }
326
327 pub fn disconnect(&self, keep_running: bool) -> Result<()> {
330 let net = self.net_record()?;
331 let (path, body, timeout) = ops::disconnect(&net, keep_running);
332 self.call(&path, body, timeout).map(|_| ())
333 }
334
335 pub fn reset(&self, halt: bool) -> Result<()> {
337 let net = self.net_record()?;
338 let (path, body, timeout) = ops::reset(&net, halt);
339 self.call(&path, body, timeout).map(|_| ())
340 }
341
342 pub fn erase(&self) -> Result<()> {
344 let net = self.net_record()?;
345 let (path, body, timeout) = ops::erase(&net);
346 self.call(&path, body, timeout).map(|_| ())
347 }
348
349 pub fn flash(&self, firmware_path: impl AsRef<Path>) -> Result<()> {
353 let path = firmware_path.as_ref();
354 let (contents, kind) = read_firmware(path)?;
355 self.flash_bytes(&contents, kind, None)
356 }
357
358 pub fn flash_bin(&self, firmware_path: impl AsRef<Path>, address: u32) -> Result<()> {
360 let contents = std::fs::read(firmware_path.as_ref())
361 .map_err(|e| Error::Config(format!("cannot read firmware: {e}")))?;
362 self.flash_bytes(&contents, FirmwareKind::Bin, Some(address))
363 }
364
365 pub fn flash_bytes(
367 &self,
368 contents: &[u8],
369 kind: FirmwareKind,
370 address: Option<u32>,
371 ) -> Result<()> {
372 let net = self.net_record()?;
373 let (path, body, timeout) = ops::flash(&net, contents, kind, address);
374 self.call(&path, body, timeout).map(|_| ())
375 }
376
377 pub fn read_memory(&self, address: u64, length: usize) -> Result<Vec<u8>> {
379 let net = self.net_record()?;
380 let (path, body, timeout) = ops::read_memory(&net, address, length);
381 let resp = self.call(&path, body, timeout)?;
382 wire::debug_memory_bytes(&resp)
383 }
384
385 pub fn info(&self) -> Result<DebugInfo> {
387 let net = self.net_record()?;
388 let (path, body, timeout) = ops::info(&net);
389 let resp = self.call(&path, body, timeout)?;
390 serde_json::from_value(resp).map_err(Into::into)
391 }
392
393 pub fn status(&self) -> Result<DebugStatus> {
395 let net = self.net_record()?;
396 let (path, body, timeout) = ops::status(&net);
397 let resp = self.call(&path, body, timeout)?;
398 serde_json::from_value(resp).map_err(Into::into)
399 }
400
401 pub fn rtt(&self) -> Result<RttStream> {
407 self.rtt_with(&RttOptions::default())
408 }
409
410 pub fn rtt_with(&self, opts: &RttOptions) -> Result<RttStream> {
412 let net = self.net_record()?;
413 let body = ops::rtt_body(&net, opts);
414 let req = wire::debug_request("/debug/rtt", body, Timeout::Unbounded);
415 let reader = self.client.stream_debug(&req)?;
416 Ok(RttStream { reader })
417 }
418
419 #[cfg(feature = "rtt")]
450 pub fn rtt_interactive(&self) -> Result<crate::nets::rtt::RttSession> {
451 self.rtt_interactive_with(&RttOptions::default())
452 }
453
454 #[cfg(feature = "rtt")]
458 pub fn rtt_interactive_with(&self, opts: &RttOptions) -> Result<crate::nets::rtt::RttSession> {
459 crate::nets::rtt::RttSession::open(
460 self.client.base_url(),
461 self.name.clone(),
462 opts,
463 self.client.current_token(),
464 )
465 }
466}
467
468#[cfg(feature = "blocking")]
471pub struct RttStream {
472 reader: Box<dyn std::io::Read + Send + Sync>,
473}
474
475#[cfg(feature = "blocking")]
476impl std::io::Read for RttStream {
477 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
478 self.reader.read(buf)
479 }
480}
481
482#[cfg(feature = "async")]
494#[derive(Clone)]
495pub struct AsyncDebugNet<'a> {
496 pub(crate) client: &'a crate::async_client::AsyncLagerBox,
497 pub(crate) name: String,
498 pub(crate) record: Arc<Mutex<Option<Value>>>,
499}
500
501#[cfg(feature = "async")]
502impl AsyncDebugNet<'_> {
503 pub fn name(&self) -> &str {
505 &self.name
506 }
507
508 async fn net_record(&self) -> Result<Value> {
509 if let Some(rec) = self.record.lock().unwrap().clone() {
510 return Ok(rec);
511 }
512 let rec = self.client.debug_net_record(&self.name).await?;
513 *self.record.lock().unwrap() = Some(rec.clone());
514 Ok(rec)
515 }
516
517 async fn call(&self, path: &str, body: Value, timeout: Timeout) -> Result<Value> {
518 let req = wire::debug_request(path, body, timeout);
519 let result = match self.client.execute_debug(&req).await {
520 Ok((status, resp)) => wire::parse_debug(status, resp),
521 Err(e) => Err(e),
522 };
523 if result.is_err() {
524 *self.record.lock().unwrap() = None;
527 }
528 result
529 }
530
531 pub async fn connect(&self) -> Result<DebugConnection> {
533 self.connect_with(&ConnectOptions::default()).await
534 }
535
536 pub async fn connect_with(&self, opts: &ConnectOptions) -> Result<DebugConnection> {
538 let net = self.net_record().await?;
539 let (path, body, timeout) = ops::connect(&net, opts);
540 let resp = self.call(&path, body, timeout).await?;
541 serde_json::from_value(resp).map_err(Into::into)
542 }
543
544 pub async fn disconnect(&self, keep_running: bool) -> Result<()> {
546 let net = self.net_record().await?;
547 let (path, body, timeout) = ops::disconnect(&net, keep_running);
548 self.call(&path, body, timeout).await.map(|_| ())
549 }
550
551 pub async fn reset(&self, halt: bool) -> Result<()> {
553 let net = self.net_record().await?;
554 let (path, body, timeout) = ops::reset(&net, halt);
555 self.call(&path, body, timeout).await.map(|_| ())
556 }
557
558 pub async fn erase(&self) -> Result<()> {
560 let net = self.net_record().await?;
561 let (path, body, timeout) = ops::erase(&net);
562 self.call(&path, body, timeout).await.map(|_| ())
563 }
564
565 pub async fn flash(&self, firmware_path: impl AsRef<Path>) -> Result<()> {
567 let (contents, kind) = read_firmware(firmware_path.as_ref())?;
568 self.flash_bytes(&contents, kind, None).await
569 }
570
571 pub async fn flash_bin(&self, firmware_path: impl AsRef<Path>, address: u32) -> Result<()> {
573 let contents = std::fs::read(firmware_path.as_ref())
574 .map_err(|e| Error::Config(format!("cannot read firmware: {e}")))?;
575 self.flash_bytes(&contents, FirmwareKind::Bin, Some(address)).await
576 }
577
578 pub async fn flash_bytes(
580 &self,
581 contents: &[u8],
582 kind: FirmwareKind,
583 address: Option<u32>,
584 ) -> Result<()> {
585 let net = self.net_record().await?;
586 let (path, body, timeout) = ops::flash(&net, contents, kind, address);
587 self.call(&path, body, timeout).await.map(|_| ())
588 }
589
590 pub async fn read_memory(&self, address: u64, length: usize) -> Result<Vec<u8>> {
592 let net = self.net_record().await?;
593 let (path, body, timeout) = ops::read_memory(&net, address, length);
594 let resp = self.call(&path, body, timeout).await?;
595 wire::debug_memory_bytes(&resp)
596 }
597
598 pub async fn info(&self) -> Result<DebugInfo> {
600 let net = self.net_record().await?;
601 let (path, body, timeout) = ops::info(&net);
602 let resp = self.call(&path, body, timeout).await?;
603 serde_json::from_value(resp).map_err(Into::into)
604 }
605
606 pub async fn status(&self) -> Result<DebugStatus> {
608 let net = self.net_record().await?;
609 let (path, body, timeout) = ops::status(&net);
610 let resp = self.call(&path, body, timeout).await?;
611 serde_json::from_value(resp).map_err(Into::into)
612 }
613}
614
615#[cfg(test)]
616mod tests {
617 use super::*;
618
619 #[test]
620 fn base64_matches_reference() {
621 assert_eq!(base64_encode(b""), "");
622 assert_eq!(base64_encode(b"f"), "Zg==");
623 assert_eq!(base64_encode(b"fo"), "Zm8=");
624 assert_eq!(base64_encode(b"foo"), "Zm9v");
625 assert_eq!(base64_encode(b"foob"), "Zm9vYg==");
626 assert_eq!(base64_encode(b"fooba"), "Zm9vYmE=");
627 assert_eq!(base64_encode(b"foobar"), "Zm9vYmFy");
628 assert_eq!(base64_encode(&[0x00, 0xff, 0x10]), "AP8Q");
629 }
630
631 #[test]
632 fn firmware_kind_from_extension() {
633 assert_eq!(
634 FirmwareKind::from_path(Path::new("a/b/fw.hex")).unwrap(),
635 FirmwareKind::Hex
636 );
637 assert_eq!(
638 FirmwareKind::from_path(Path::new("FW.ELF")).unwrap(),
639 FirmwareKind::Elf
640 );
641 assert!(FirmwareKind::from_path(Path::new("fw.txt")).is_err());
642 }
643
644 #[test]
645 fn debug_body_wraps_net_and_params() {
646 let net = json!({"name": "debug1", "role": "debug", "pin": "nrf52"});
647 let body = debug_body(&net, json!({"halt": true}));
648 assert_eq!(body["net"]["name"], "debug1");
649 assert_eq!(body["halt"], true);
650 }
651}