replace tools workspace with xtask tasks

This commit is contained in:
Jorge Aparicio 2021-01-25 16:10:54 +01:00
parent 80aeec1551
commit b08a82bacd
14 changed files with 182 additions and 170 deletions

View file

@ -0,0 +1,2 @@
[alias]
xtask = "run --manifest-path ../xtask/Cargo.toml --"

View file

@ -0,0 +1,2 @@
[alias]
xtask = "run --manifest-path ../xtask/Cargo.toml --"

View file

@ -1,7 +0,0 @@
[workspace]
members = [
"change-channel",
"dongle-flash",
"serial-term",
"usb-list",
]

View file

@ -1,12 +0,0 @@
[package]
authors = ["Jorge Aparicio <jorge.aparicio@ferrous-systems.com>"]
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" }

View file

@ -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::<Vec<_>>();
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::<u8>()?;
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
}

View file

@ -1,13 +0,0 @@
[package]
authors = ["Jorge Aparicio <jorge.aparicio@ferrous-systems.com>"]
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"

View file

@ -1,12 +0,0 @@
[package]
authors = ["Jorge Aparicio <jorge.aparicio@ferrous-systems.com>"]
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"

View file

@ -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(())
}

View file

@ -1,11 +0,0 @@
[package]
authors = ["Jorge Aparicio <jorge.aparicio@ferrous-systems.com>"]
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"

View file

@ -1,19 +0,0 @@
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
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(())
}

17
xtask/Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[package]
authors = ["Jorge Aparicio <jorge.aparicio@ferrous-systems.com>"]
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"

1
xtask/src/config.rs Normal file
View file

@ -0,0 +1 @@
pub const DONGLE_PATH: &[&str] = &["boards", "dongle"];

58
xtask/src/main.rs Normal file
View file

@ -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::<Vec<_>>();
let args = args.iter().map(|arg| &arg[..]).collect::<Vec<_>>();
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<PathBuf> {
// 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())
}

View file

@ -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,
};
use crate::config;
pub fn change_channel(channel: &str) -> color_eyre::Result<()> {
fn check_pid(pid: u16) -> bool {
pid == pids::LOOPBACK || pid == pids::PUZZLE
}
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::<u8>()?;
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;
fn main() -> Result<(), anyhow::Error> {
let args = env::args().skip(1 /* program name */).collect::<Vec<_>>();
ensure!(args.len() == 1, "expected exactly one argument");
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(())
}