From b08a82bacd1cf350056663323f9791950a5dc885 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 25 Jan 2021 16:10:54 +0100 Subject: [PATCH] replace tools workspace with xtask tasks --- advanced/.cargo/config.toml | 2 + beginner/.cargo/config.toml | 2 + tools/Cargo.toml | 7 -- tools/change-channel/Cargo.toml | 12 -- tools/change-channel/src/main.rs | 33 ----- tools/dongle-flash/Cargo.toml | 13 -- tools/serial-term/Cargo.toml | 12 -- tools/serial-term/src/main.rs | 52 -------- tools/usb-list/Cargo.toml | 11 -- tools/usb-list/src/main.rs | 19 --- xtask/Cargo.toml | 17 +++ xtask/src/config.rs | 1 + xtask/src/main.rs | 58 +++++++++ .../src/main.rs => xtask/src/tasks.rs | 113 ++++++++++++++++-- 14 files changed, 182 insertions(+), 170 deletions(-) create mode 100644 advanced/.cargo/config.toml create mode 100644 beginner/.cargo/config.toml delete mode 100644 tools/Cargo.toml delete mode 100644 tools/change-channel/Cargo.toml delete mode 100644 tools/change-channel/src/main.rs delete mode 100644 tools/dongle-flash/Cargo.toml delete mode 100644 tools/serial-term/Cargo.toml delete mode 100644 tools/serial-term/src/main.rs delete mode 100644 tools/usb-list/Cargo.toml delete mode 100644 tools/usb-list/src/main.rs create mode 100644 xtask/Cargo.toml create mode 100644 xtask/src/config.rs create mode 100644 xtask/src/main.rs rename tools/dongle-flash/src/main.rs => xtask/src/tasks.rs (58%) diff --git a/advanced/.cargo/config.toml b/advanced/.cargo/config.toml new file mode 100644 index 0000000..4c4dd70 --- /dev/null +++ b/advanced/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "run --manifest-path ../xtask/Cargo.toml --" \ No newline at end of file diff --git a/beginner/.cargo/config.toml b/beginner/.cargo/config.toml new file mode 100644 index 0000000..4c4dd70 --- /dev/null +++ b/beginner/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "run --manifest-path ../xtask/Cargo.toml --" \ No newline at end of file diff --git a/tools/Cargo.toml b/tools/Cargo.toml deleted file mode 100644 index 031aa66..0000000 --- a/tools/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[workspace] -members = [ - "change-channel", - "dongle-flash", - "serial-term", - "usb-list", -] \ No newline at end of file diff --git a/tools/change-channel/Cargo.toml b/tools/change-channel/Cargo.toml deleted file mode 100644 index 0a2cd35..0000000 --- a/tools/change-channel/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -authors = ["Jorge Aparicio "] -edition = "2018" -license = "MIT OR Apache-2.0" -name = "change-channel" -version = "0.0.0" - -[dependencies] -anyhow = "1.0.27" -consts = { path = "../../advanced/common/consts" } -hidapi = "1.2.2" -pids = { path = "../../common/pids" } \ No newline at end of file diff --git a/tools/change-channel/src/main.rs b/tools/change-channel/src/main.rs deleted file mode 100644 index 938def1..0000000 --- a/tools/change-channel/src/main.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::env; - -use anyhow::{anyhow, bail, ensure}; -use hidapi::HidApi; - -fn main() -> Result<(), anyhow::Error> { - let args = env::args() - .skip(1) // skip program name - .collect::>(); - ensure!(!args.is_empty(), "expected exactly one argument"); - - let api = HidApi::new()?; - let dev = api - .device_list() - .filter(|dev| dev.vendor_id() == consts::VID && check_pid(dev.product_id())) - .next() - .ok_or_else(|| anyhow!("device not found"))? - .open_device(&api)?; - - let chan = args[0].parse::()?; - if chan < 11 || chan > 26 { - bail!("channel is out of range (`11..=26`)") - } - const REPORT_ID: u8 = 0; - dev.write(&[REPORT_ID, chan])?; - println!("requested channel change to channel {}", chan); - - Ok(()) -} - -fn check_pid(pid: u16) -> bool { - pid == pids::LOOPBACK || pid == pids::PUZZLE -} diff --git a/tools/dongle-flash/Cargo.toml b/tools/dongle-flash/Cargo.toml deleted file mode 100644 index 2779d26..0000000 --- a/tools/dongle-flash/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -authors = ["Jorge Aparicio "] -edition = "2018" -license = "MIT OR Apache-2.0" -name = "dongle-flash" -version = "0.0.0" - -[dependencies] -anyhow = "1.0.31" -ihex = "1.1.2" -serialport = "3.3.0" -tempfile = "3.1.0" -xmas-elf = "0.7.0" diff --git a/tools/serial-term/Cargo.toml b/tools/serial-term/Cargo.toml deleted file mode 100644 index fa4cd42..0000000 --- a/tools/serial-term/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -authors = ["Jorge Aparicio "] -edition = "2018" -license = "MIT OR Apache-2.0" -name = "serial-term" -version = "0.0.0" - -[dependencies] -anyhow = "1.0.30" -consts = { path = "../../advanced/common/consts" } -ctrlc = "3.1.4" -serialport = "3.3.0" \ No newline at end of file diff --git a/tools/serial-term/src/main.rs b/tools/serial-term/src/main.rs deleted file mode 100644 index 7f328d3..0000000 --- a/tools/serial-term/src/main.rs +++ /dev/null @@ -1,52 +0,0 @@ -use core::sync::atomic::{AtomicBool, Ordering}; -use std::{ - io::{self, Read as _, Write as _}, - thread, - time::Duration, -}; - -use serialport::SerialPortType; - -fn main() -> Result<(), anyhow::Error> { - let mut once = true; - let dongle = loop { - if let Some(dongle) = serialport::available_ports()? - .into_iter() - .filter(|info| match &info.port_type { - SerialPortType::UsbPort(usb) => usb.vid == consts::VID, - _ => false, - }) - .next() - { - break dongle; - } else if once { - once = false; - - eprintln!("(waiting for the Dongle to be connected)"); - } - }; - - let mut port = serialport::open(&dongle.port_name)?; - - static CONTINUE: AtomicBool = AtomicBool::new(true); - - // properly close the serial device on Ctrl-C - ctrlc::set_handler(|| CONTINUE.store(false, Ordering::Relaxed))?; - - let stdout = io::stdout(); - let mut stdout = stdout.lock(); - let mut read_buf = [0; 64]; - while CONTINUE.load(Ordering::Relaxed) { - if port.bytes_to_read()? != 0 { - let n = port.read(&mut read_buf)?; - stdout.write_all(&read_buf[..n])?; - stdout.flush()?; - } else { - // time span between two consecutive FS USB packets - thread::sleep(Duration::from_millis(1)); - } - } - - eprintln!("(closing the serial port)"); - Ok(()) -} diff --git a/tools/usb-list/Cargo.toml b/tools/usb-list/Cargo.toml deleted file mode 100644 index 6025478..0000000 --- a/tools/usb-list/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -authors = ["Jorge Aparicio "] -edition = "2018" -license = "MIT OR Apache-2.0" -name = "usb-list" -version = "0.0.0" - -[dependencies] -consts = { path = "../../advanced/common/consts" } -pids = { path = "../../common/pids" } -rusb = "0.5.5" diff --git a/tools/usb-list/src/main.rs b/tools/usb-list/src/main.rs deleted file mode 100644 index 4068a52..0000000 --- a/tools/usb-list/src/main.rs +++ /dev/null @@ -1,19 +0,0 @@ -use std::error::Error; - -fn main() -> Result<(), Box> { - for dev in rusb::devices()?.iter() { - let desc = dev.device_descriptor()?; - let suffix = match (desc.vendor_id(), desc.product_id()) { - (0x1366, 0x1015) => " <- J-Link on the nRF52840 Development Kit", - (0x1915, 0x521f) => " <- nRF52840 Dongle (in bootloader mode)", - (0x2020, pids::LOOPBACK) => " <- nRF52840 Dongle (loopback.hex)", - (0x2020, pids::PUZZLE) => " <- nRF52840 Dongle (puzzle.hex)", - (consts::VID, consts::PID) => " <- nRF52840 on the nRF52840 Development Kit", - _ => "", - }; - - println!("{:?}{}", dev, suffix); - } - - Ok(()) -} diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 0000000..2bd3968 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,17 @@ +[package] +authors = ["Jorge Aparicio "] +edition = "2018" +name = "xtask" +version = "0.0.0" + +[dependencies] +color-eyre = "0.5.10" +consts = { path = "../advanced/common/consts" } +ctrlc = "3.1.4" +hidapi = "1.2.2" +ihex = "1.1.2" +pids = { path = "../common/pids" } +rusb = "0.5.5" +serialport = "3.3.0" +tempfile = "3.2.0" +xmas-elf = "0.7.0" \ No newline at end of file diff --git a/xtask/src/config.rs b/xtask/src/config.rs new file mode 100644 index 0000000..b1a711f --- /dev/null +++ b/xtask/src/config.rs @@ -0,0 +1 @@ +pub const DONGLE_PATH: &[&str] = &["boards", "dongle"]; diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 0000000..bcf4759 --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,58 @@ +#![deny(warnings)] + +mod config; +mod tasks; + +use std::{env, path::PathBuf}; + +fn main() -> color_eyre::Result<()> { + color_eyre::install()?; + + // first arg is the name of the executable; skip it + let args = env::args().skip(1).collect::>(); + let args = args.iter().map(|arg| &arg[..]).collect::>(); + + match &args[..] { + ["change-channel", channel] => tasks::change_channel(channel), + ["dongle-flash", hex] => tasks::dongle_flash(hex), + ["serial-term"] => tasks::serial_term(), + ["usb-list"] => tasks::usb_list(), + _ => { + eprintln!( + "cargo xtask +Workshop-specific tools + +USAGE: + cargo xtask [COMMAND] + +COMMANDS: + change-channel [NUMBER] instructs the nRF Dongle to listen to a different radio channel + dongle-flash [PATH] flashes the hex file on the dongle (NOTE PATH is relative to the boards/dongle directory) + serial-term displays the log output of the Dongle + usb-list list all connected USB devices; highlights workshop devices +" + ); + + Ok(()) + } + } +} + +// changes directory into the given path, relative to the root of the repository +fn cd(segments: &[&str]) -> color_eyre::Result<()> { + let mut path = repository_root()?; + for segment in segments { + path.push(segment); + } + env::set_current_dir(path)?; + Ok(()) +} + +fn repository_root() -> color_eyre::Result { + // path to this crate (the directory that contains this crate's Cargo.toml) + Ok(PathBuf::from(env::var("CARGO_MANIFEST_DIR")?) + // from there go one level up + .parent() + .unwrap() + .to_owned()) +} diff --git a/tools/dongle-flash/src/main.rs b/xtask/src/tasks.rs similarity index 58% rename from tools/dongle-flash/src/main.rs rename to xtask/src/tasks.rs index bee2a78..acd994b 100644 --- a/tools/dongle-flash/src/main.rs +++ b/xtask/src/tasks.rs @@ -1,11 +1,16 @@ -use core::convert::TryFrom; use std::{ - env, fs, + convert::TryFrom, + fs, + io::{self, Write as _}, path::Path, process::{Command, Stdio}, + sync::atomic::{AtomicBool, Ordering}, + thread, + time::Duration, }; -use anyhow::{anyhow, bail, ensure}; +use color_eyre::eyre::{anyhow, bail, Report}; +use hidapi::HidApi; use ihex::record::Record; use serialport::SerialPortType; use tempfile::TempDir; @@ -14,13 +19,37 @@ use xmas_elf::{ ElfFile, }; -const VID: u16 = 0x1915; -const PID: u16 = 0x521f; +use crate::config; -fn main() -> Result<(), anyhow::Error> { - let args = env::args().skip(1 /* program name */).collect::>(); +pub fn change_channel(channel: &str) -> color_eyre::Result<()> { + fn check_pid(pid: u16) -> bool { + pid == pids::LOOPBACK || pid == pids::PUZZLE + } - ensure!(args.len() == 1, "expected exactly one argument"); + let api = HidApi::new()?; + let dev = api + .device_list() + .filter(|dev| dev.vendor_id() == consts::VID && check_pid(dev.product_id())) + .next() + .ok_or_else(|| anyhow!("device not found"))? + .open_device(&api)?; + + let chan = channel.parse::()?; + if chan < 11 || chan > 26 { + bail!("channel is out of range (`11..=26`)") + } + const REPORT_ID: u8 = 0; + dev.write(&[REPORT_ID, chan])?; + println!("requested channel change to channel {}", chan); + + Ok(()) +} + +pub fn dongle_flash(hex: &str) -> color_eyre::Result<()> { + const VID: u16 = 0x1915; + const PID: u16 = 0x521f; + + crate::cd(config::DONGLE_PATH)?; let dongle = serialport::available_ports()? .into_iter() @@ -35,7 +64,7 @@ connect the Dongle to your laptop or PC and press the reset button to put it in if the red LED was blinking and you got this message then the device wasn't correctly enumerated; remove it and try again") })?; - let path = Path::new(&args[0]); + let path = Path::new(hex); let filename = Path::new( path.file_name() .ok_or_else(|| anyhow!("{} has no file name", path.display()))?, @@ -52,13 +81,13 @@ if the red LED was blinking and you got this message then the device wasn't corr // here we map the ELF loadable segments -- these correspond to sections like `.text`, `.rodata` // and `.data` (initial values) -- to ihex records let bytes = fs::read(path)?; - let elf_file = ElfFile::new(&bytes).map_err(anyhow::Error::msg)?; + let elf_file = ElfFile::new(&bytes).map_err(Report::msg)?; let mut records = vec![]; for ph in elf_file.program_iter() { if ph.get_type() == Ok(Type::Load) { let start = ph.physical_addr(); - match ph.get_data(&elf_file).map_err(anyhow::Error::msg)? { + match ph.get_data(&elf_file).map_err(Report::msg)? { SegmentData::Undefined(bytes) => { // this is what `objcopy -O ihex` uses and it works with `nrfutil` const RECORD_SIZE: usize = 16; @@ -148,3 +177,65 @@ if the red LED was blinking and you got this message then the device wasn't corr Ok(()) } + +pub fn serial_term() -> color_eyre::Result<()> { + let mut once = true; + let dongle = loop { + if let Some(dongle) = serialport::available_ports()? + .into_iter() + .filter(|info| match &info.port_type { + SerialPortType::UsbPort(usb) => usb.vid == consts::VID, + _ => false, + }) + .next() + { + break dongle; + } else if once { + once = false; + + eprintln!("(waiting for the Dongle to be connected)"); + } + }; + + let mut port = serialport::open(&dongle.port_name)?; + + static CONTINUE: AtomicBool = AtomicBool::new(true); + + // properly close the serial device on Ctrl-C + ctrlc::set_handler(|| CONTINUE.store(false, Ordering::Relaxed))?; + + let stdout = io::stdout(); + let mut stdout = stdout.lock(); + let mut read_buf = [0; 64]; + while CONTINUE.load(Ordering::Relaxed) { + if port.bytes_to_read()? != 0 { + let n = port.read(&mut read_buf)?; + stdout.write_all(&read_buf[..n])?; + stdout.flush()?; + } else { + // time span between two consecutive FS USB packets + thread::sleep(Duration::from_millis(1)); + } + } + + eprintln!("(closing the serial port)"); + Ok(()) +} + +pub fn usb_list() -> color_eyre::Result<()> { + for dev in rusb::devices()?.iter() { + let desc = dev.device_descriptor()?; + let suffix = match (desc.vendor_id(), desc.product_id()) { + (0x1366, 0x1015) => " <- J-Link on the nRF52840 Development Kit", + (0x1915, 0x521f) => " <- nRF52840 Dongle (in bootloader mode)", + (0x2020, pids::LOOPBACK) => " <- nRF52840 Dongle (loopback.hex)", + (0x2020, pids::PUZZLE) => " <- nRF52840 Dongle (puzzle.hex)", + (consts::VID, consts::PID) => " <- nRF52840 on the nRF52840 Development Kit", + _ => "", + }; + + println!("{:?}{}", dev, suffix); + } + + Ok(()) +}