blob: 76a0a4e8b7482268750128fa951dee20f71664e5 [file] [log] [blame] [edit]
use core::{str, task};
use std::collections::HashSet;
use anyhow::{anyhow, Result};
use clap::{Parser, Subcommand, ValueEnum};
use env_logger::Target;
use futures_lite::future::block_on;
use log::{debug, error, info};
use nusb::{
transfer::{ControlIn, ControlOut, ControlType, Recipient},
Interface,
};
use serde::{Deserialize, Serialize};
#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Clone, Copy, Debug, ValueEnum)]
enum SwdTarget {
Dut,
Buddy,
}
#[derive(Clone, Subcommand)]
enum Commands {
/// Query test target for info.
Info,
/// Sets the Test Status RGB LED color.
SetLed { red: u8, green: u8, blue: u8 },
/// Selects which target is connected to the probe.
Target { target: SwdTarget },
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
enum TestTargetCapability {
Uart,
Spi,
I2c,
DigitalIo,
Analog,
}
#[derive(Debug, Deserialize, Serialize)]
struct TestTargetInfo {
/// Name of the test target.
name: String,
/// MCU of the device under test.
mcu: String,
/// Capabilities of the test target board.
capabilities: Vec<TestTargetCapability>,
}
const RPI_VENDOR_ID: u16 = 0x2e8a;
const DEBUG_PROBE_PRODUCT_ID: u16 = 0x000c;
struct PigweedTestTarget {
interface: Interface,
}
#[derive(Debug)]
#[repr(u16)]
enum PigweedVendorCommand {
Hello = 0,
SetTarget = 1,
GetTargetInfo = 2,
SetLed = 3,
}
impl PigweedTestTarget {
pub fn new() -> Result<Self> {
let device_info = nusb::list_devices()?
.find(|d| d.vendor_id() == RPI_VENDOR_ID && d.product_id() == DEBUG_PROBE_PRODUCT_ID)
.ok_or_else(|| anyhow!("Could not find debug probe"))?;
let device = device_info.open()?;
let interface = device.claim_interface(0)?;
debug!("{:?}", PigweedVendorCommand::Hello as u16);
let result = block_on(interface.control_in(ControlIn {
control_type: ControlType::Vendor,
recipient: Recipient::Device,
request: 0x50,
value: 0x0,
index: PigweedVendorCommand::Hello as u16,
length: 256,
}));
if let Err(e) = result.status {
return Err(anyhow!("Could not query Pigweed vendor interface: {e}"));
}
if &result.data != b"PIGWEED" {
info!("{result:?}");
return Err(anyhow!(
"Pigweed vendor interface did not respond correctly"
));
}
Ok(Self { interface })
}
pub fn set_target(&mut self, target: SwdTarget) -> Result<()> {
let value = match target {
SwdTarget::Dut => 0,
SwdTarget::Buddy => 1,
};
let result = block_on(self.interface.control_out(ControlOut {
control_type: ControlType::Vendor,
recipient: Recipient::Device,
request: 0x50,
value,
index: PigweedVendorCommand::SetTarget as u16,
data: &[],
}));
if let Err(e) = result.status {
return Err(anyhow!("Failed to set target to {target:?}: {e}"));
}
Ok(())
}
pub fn get_info(&mut self) -> Result<TestTargetInfo> {
let result = block_on(self.interface.control_in(ControlIn {
control_type: ControlType::Vendor,
recipient: Recipient::Device,
request: 0x50,
value: 0x0,
index: PigweedVendorCommand::GetTargetInfo as u16,
length: 256,
}));
if let Err(e) = result.status {
return Err(anyhow!("Could not query Pigweed target info: {e}"));
}
let json = str::from_utf8(&result.data)?;
debug!("json info string: {json}");
let info = serde_json::from_str(json)?;
Ok(info)
}
pub fn set_led(&mut self, red: u8, green: u8, blue: u8) -> Result<()> {
let result = block_on(self.interface.control_out(ControlOut {
control_type: ControlType::Vendor,
recipient: Recipient::Device,
request: 0x50,
value: 0x0,
index: PigweedVendorCommand::SetLed as u16,
data: &[red, green, blue],
}));
if let Err(e) = result.status {
return Err(anyhow!("Failed to set target to test status LED: {e}"));
}
Ok(())
}
}
fn info_command() -> Result<()> {
let mut target = PigweedTestTarget::new()?;
let info = target.get_info()?;
println!("Board Name: {}", info.name);
println!("Microcontroller: {}", info.mcu);
println!("Capabilities:");
for cap in &info.capabilities {
println!(" - {cap:?}");
}
Ok(())
}
fn set_led_command(red: u8, green: u8, blue: u8) -> Result<()> {
let mut target = PigweedTestTarget::new()?;
target.set_led(red, green, blue)?;
println!("Target status LED set to #{red:02x}{green:02x}{blue:02x}");
Ok(())
}
fn target_command(swd_target: SwdTarget) -> Result<()> {
let mut target = PigweedTestTarget::new()?;
target.set_target(swd_target)?;
println!("SWD Target set to {swd_target:?}");
Ok(())
}
fn main() {
env_logger::init();
let cli = Cli::parse();
let result = match cli.command {
Commands::Info => info_command(),
Commands::SetLed { red, green, blue } => set_led_command(red, green, blue),
Commands::Target { target } => target_command(target),
};
if let Err(e) = result {
println!("command failed: {e}");
std::process::exit(1);
}
}