Skip to content
This repository was archived by the owner on Jan 30, 2024. It is now read-only.

Commit ceab096

Browse files
committed
RTT input support
1 parent 538e3c7 commit ceab096

File tree

4 files changed

+183
-41
lines changed

4 files changed

+183
-41
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ probe-rs-rtt = "0.3.0"
2626
rustc-demangle = "0.1.16"
2727
signal-hook = "0.1.16"
2828
structopt = "0.3.15"
29+
libc = "0.2.77"
30+
31+
[target.'cfg(windows)'.dependencies]
32+
winapi = { version = "0.3.8", features = ["winbase", "consoleapi", "processenv", "handleapi", "synchapi", "impl-default"] }
2933

3034
[features]
3135
defmt = ["defmt-elf2table", "defmt-decoder"]

src/main.rs

Lines changed: 82 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
mod logger;
22

3+
#[cfg(windows)]
4+
#[path ="reader_windows.rs"]
5+
mod reader;
6+
7+
#[cfg(unix)]
8+
#[path ="reader_unix.rs"]
9+
mod reader;
10+
311
use core::{
412
cmp,
513
convert::TryInto,
@@ -10,7 +18,7 @@ use std::{
1018
borrow::Cow,
1119
collections::{btree_map, BTreeMap, HashSet},
1220
fs,
13-
io::{self, Write as _},
21+
io::{self, Read as _, Write as _},
1422
path::{Path, PathBuf},
1523
process,
1624
sync::{Arc, Mutex},
@@ -34,7 +42,7 @@ use probe_rs::{
3442
flashing::{self, Format},
3543
Core, CoreRegisterAddress, DebugProbeInfo, DebugProbeSelector, MemoryInterface, Probe, Session,
3644
};
37-
use probe_rs_rtt::{Rtt, ScanRegion, UpChannel};
45+
use probe_rs_rtt::{Rtt, ScanRegion};
3846
use structopt::StructOpt;
3947

4048
const TIMEOUT: Duration = Duration::from_secs(1);
@@ -59,9 +67,13 @@ struct Opts {
5967

6068
/// Enable defmt decoding.
6169
#[cfg(feature = "defmt")]
62-
#[structopt(long, conflicts_with = "no_flash")]
70+
#[structopt(long)]
6371
defmt: bool,
6472

73+
/// Enable input RTT channel (host to target)
74+
#[structopt(long)]
75+
input: bool,
76+
6577
/// The chip to program.
6678
#[structopt(long, required_unless_one(&["list-chips", "list-probes"]), env = "PROBE_RUN_CHIP")]
6779
chip: Option<String>,
@@ -75,7 +87,7 @@ struct Opts {
7587
elf: Option<PathBuf>,
7688

7789
/// Skip writing the application binary to flash.
78-
#[structopt(long, conflicts_with = "defmt")]
90+
#[structopt(long)]
7991
no_flash: bool,
8092

8193
/// Enable more verbose logging.
@@ -360,18 +372,62 @@ fn notmain() -> Result<i32, anyhow::Error> {
360372
let sig_id = signal_hook::flag::register(signal_hook::SIGINT, exit.clone())?;
361373

362374
let sess = Arc::new(Mutex::new(sess));
363-
let mut logging_channel = setup_logging_channel(rtt_addr, sess.clone())?;
375+
376+
let (mut logging_channel, mut input_channel) = if let Some(rtt_addr) = rtt_addr {
377+
let mut rtt = setup_rtt(rtt_addr, sess.clone())?;
378+
379+
let logging_channel = rtt
380+
.up_channels()
381+
.take(0)
382+
.ok_or_else(|| anyhow!("RTT up channel 0 not found"))?;
383+
384+
let input_channel = if opts.input {
385+
Some(
386+
rtt.down_channels()
387+
.take(0)
388+
.ok_or_else(|| anyhow!("RTT down channel 0 not found"))?,
389+
)
390+
} else {
391+
None
392+
};
393+
394+
(Some(logging_channel), input_channel)
395+
} else {
396+
eprintln!("RTT logs not available; blocking until the device halts..");
397+
(None, None)
398+
};
364399

365400
// wait for breakpoint
366401
let stdout = io::stdout();
367402
let mut stdout = stdout.lock();
368403
let mut read_buf = [0; 1024];
369404
#[cfg(feature = "defmt")]
370405
let mut frames = vec![];
406+
407+
let rd = reader::Reader::new();
408+
let mut write_buf = [0; 1024];
409+
let mut write_start = 0;
410+
let mut write_end = 0;
411+
371412
let mut was_halted = false;
372413
let current_dir = std::env::current_dir()?;
373414
// TODO strip prefix from crates-io paths (?)
374415
while !exit.load(Ordering::Relaxed) {
416+
if let Some(input_channel) = &mut input_channel {
417+
// If we have no pending data, read some.
418+
if write_start == write_end {
419+
let n = rd.read(&mut write_buf);
420+
write_start = 0;
421+
write_end = n;
422+
}
423+
424+
// If we have pending data, try to write it.
425+
if write_start != write_end {
426+
let n = input_channel.write(&write_buf[write_start..write_end])?;
427+
write_start += n;
428+
}
429+
}
430+
375431
if let Some(logging_channel) = &mut logging_channel {
376432
let num_bytes_read = match logging_channel.read(&mut read_buf) {
377433
Ok(n) => n,
@@ -515,46 +571,31 @@ enum TopException {
515571
Other,
516572
}
517573

518-
fn setup_logging_channel(
519-
rtt_addr: Option<u32>,
520-
sess: Arc<Mutex<Session>>,
521-
) -> Result<Option<UpChannel>, anyhow::Error> {
522-
if let Some(rtt_addr_res) = rtt_addr {
523-
const NUM_RETRIES: usize = 10; // picked at random, increase if necessary
524-
let mut rtt_res: Result<Rtt, probe_rs_rtt::Error> =
525-
Err(probe_rs_rtt::Error::ControlBlockNotFound);
526-
527-
for try_index in 0..=NUM_RETRIES {
528-
rtt_res = Rtt::attach_region(sess.clone(), &ScanRegion::Exact(rtt_addr_res));
529-
match rtt_res {
530-
Ok(_) => {
531-
log::debug!("Successfully attached RTT");
532-
break;
533-
}
534-
Err(probe_rs_rtt::Error::ControlBlockNotFound) => {
535-
if try_index < NUM_RETRIES {
536-
log::trace!("Could not attach because the target's RTT control block isn't initialized (yet). retrying");
537-
} else {
538-
log::error!("Max number of RTT attach retries exceeded.");
539-
return Err(anyhow!(probe_rs_rtt::Error::ControlBlockNotFound));
540-
}
541-
}
542-
Err(e) => {
543-
return Err(anyhow!(e));
574+
fn setup_rtt(rtt_addr: u32, sess: Arc<Mutex<Session>>) -> Result<Rtt, anyhow::Error> {
575+
const NUM_RETRIES: usize = 10; // picked at random, increase if necessary
576+
577+
for try_index in 0..=NUM_RETRIES {
578+
let rtt_res = Rtt::attach_region(sess.clone(), &ScanRegion::Exact(rtt_addr));
579+
match rtt_res {
580+
Ok(rtt) => {
581+
log::debug!("Successfully attached RTT");
582+
return Ok(rtt);
583+
}
584+
Err(probe_rs_rtt::Error::ControlBlockNotFound) => {
585+
if try_index < NUM_RETRIES {
586+
log::trace!("Could not attach because the target's RTT control block isn't initialized (yet). retrying");
587+
} else {
588+
log::error!("Max number of RTT attach retries exceeded.");
589+
return Err(anyhow!(probe_rs_rtt::Error::ControlBlockNotFound));
544590
}
545591
}
592+
Err(e) => {
593+
return Err(anyhow!(e));
594+
}
546595
}
547-
548-
let channel = rtt_res
549-
.expect("unreachable") // this block is only executed when rtt was successfully attached before
550-
.up_channels()
551-
.take(0)
552-
.ok_or_else(|| anyhow!("RTT up channel 0 not found"))?;
553-
Ok(Some(channel))
554-
} else {
555-
eprintln!("RTT logs not available; blocking until the device halts..");
556-
Ok(None)
557596
}
597+
598+
Err(anyhow!(probe_rs_rtt::Error::ControlBlockNotFound))
558599
}
559600

560601
fn gimli2probe(reg: &gimli::Register) -> CoreRegisterAddress {

src/reader_unix.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use libc::{fcntl, tcgetattr, tcsetattr, termios, ECHO, F_SETFL, ICANON, O_NONBLOCK, TCSAFLUSH};
2+
use std::io::Read;
3+
use std::io;
4+
use std::mem;
5+
6+
pub struct Reader {
7+
termios_original: termios,
8+
}
9+
10+
impl Drop for Reader {
11+
fn drop(&mut self) {
12+
unsafe {
13+
tcsetattr(0, TCSAFLUSH, &self.termios_original);
14+
}
15+
}
16+
}
17+
18+
impl Reader {
19+
pub fn new() -> Self {
20+
let termios_original = unsafe {
21+
fcntl(0, F_SETFL, O_NONBLOCK);
22+
let mut termios_original = mem::zeroed();
23+
tcgetattr(0, &mut termios_original);
24+
let mut termios = termios_original;
25+
termios.c_lflag &= !(ECHO | ICANON);
26+
tcsetattr(0, TCSAFLUSH, &termios);
27+
termios_original
28+
};
29+
30+
Self {
31+
termios_original,
32+
}
33+
}
34+
35+
pub fn read(&self, buf: &mut [u8]) -> usize {
36+
match io::stdin().read(buf) {
37+
Ok(n) => Ok(n),
38+
Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(0),
39+
Err(e) => Err(e),
40+
}.unwrap()
41+
}
42+
}

src/reader_windows.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use core::mem;
2+
3+
use winapi::ctypes::c_void;
4+
use winapi::shared::minwindef::DWORD;
5+
use winapi::shared::ntdef::NULL;
6+
use winapi::um::consoleapi::{
7+
GetConsoleMode, GetNumberOfConsoleInputEvents, ReadConsoleInputW, SetConsoleMode, WriteConsoleW,
8+
};
9+
use winapi::um::processenv::GetStdHandle;
10+
use winapi::um::winbase::{STD_INPUT_HANDLE, STD_OUTPUT_HANDLE};
11+
use winapi::um::wincon::{INPUT_RECORD, KEY_EVENT};
12+
use winapi::um::winnt::HANDLE;
13+
14+
pub struct Reader {
15+
h: HANDLE,
16+
}
17+
18+
impl Reader {
19+
pub fn new() -> Self {
20+
let h = unsafe { GetStdHandle(STD_INPUT_HANDLE) };
21+
Self { h }
22+
}
23+
24+
pub fn read(&self, buf: &mut [u8]) -> usize {
25+
unsafe {
26+
let mut got = 0;
27+
28+
while got < buf.len() {
29+
let mut n = 0;
30+
let ret = GetNumberOfConsoleInputEvents(self.h, &mut n);
31+
assert_eq!(ret, 1);
32+
33+
if n == 0 {
34+
break;
35+
}
36+
37+
let mut input = mem::zeroed();
38+
let mut n = 0;
39+
let ret = ReadConsoleInputW(self.h, &mut input, 1, &mut n);
40+
assert_eq!(ret, 1);
41+
assert_eq!(n, 1);
42+
43+
if input.EventType == KEY_EVENT && input.Event.KeyEvent().bKeyDown == 1 {
44+
let ch = *input.Event.KeyEvent().uChar.UnicodeChar() as u8;
45+
if ch != 0 {
46+
buf[got] = ch;
47+
got += 1
48+
}
49+
}
50+
}
51+
52+
got
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)