Skip to content

Commit

Permalink
Printing attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
george-cosma committed Sep 19, 2023
1 parent 474af2e commit 204b897
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 17 deletions.
61 changes: 59 additions & 2 deletions src/bootloader/attribute.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,69 @@
use super::codes::*;
use crate::errors::TockloaderError;
use std::str;

/// TODO: What exactly is an attribute?
#[derive(Debug)]
pub struct Attribute {
pub key: String,
pub value: String,
}

impl Attribute {
/// Intrepret raw bytes, according to the Tock Format (TODO: Source??)
pub fn parse_raw(bytes: Vec<u8>) -> Attribute {
todo!()
pub fn parse_raw(bytes: Vec<u8>) -> Result<Attribute, TockloaderError> {
// First 2 bytes should be:
// <ESCAPE CHAR> <RESPONSE_GET_ATTRIBUTE>

if bytes[0] != ESCAPE_CHAR {
return Err(TockloaderError::MalformedResponse(format!(
"Expected attribute to start with ESCAPE_CHAR({}), but got {}",
ESCAPE_CHAR, bytes[0]
)));
}

if bytes[1] != RESPONSE_GET_ATTRIBUTE {
return Err(TockloaderError::MalformedResponse(format!(
"Expected attribute to have second byte RESPONSE_GET_ATTRIBUTE({}), but got {}",
RESPONSE_GET_ATTRIBUTE, bytes[1]
)));
}

// The next 8 bytes will be the name of the attribute (key) with
// null bytes as padding

let key_bytes: Vec<u8> = bytes[2..10]
.iter()
.copied()
.filter(|byte| *byte != 0)
.collect();
let key = str::from_utf8(&key_bytes)
.map_err(|_| {
TockloaderError::MalformedResponse(format!(
"Failed to parse UTF-8 from key: {:?}",
key_bytes
))
})?
.to_string();

// 9-th byte is the length of the value (without padding).
let len = bytes[10];
// 10th+ bytes is the value
let val_bytes: Vec<u8> = bytes[11..(11 + len as usize)]
.iter()
.copied()
.filter(|byte| *byte != 0)
.collect();

let value = str::from_utf8(&val_bytes)
.map_err(|_| {
TockloaderError::MalformedResponse(format!(
"Failed to parse UTF-8 from value: {:?}",
val_bytes
))
})?
.to_string();

Ok(Attribute { key, value })
}
}
12 changes: 12 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ pub enum TockloaderError {
IOError(std::io::Error),
JoinError(JoinError),
StreamClosed,
Timeout,
BootloaderNotOpen,
MalformedResponse(String),
}

#[derive(Debug)]
Expand Down Expand Up @@ -38,6 +41,15 @@ impl fmt::Display for TockloaderError {
TockloaderError::StreamClosed => {
f.write_str("The serial stream unexpectedly closed.")
},
TockloaderError::Timeout => {
f.write_str("The operation timed out. Check if the board is still connected.")
},
TockloaderError::BootloaderNotOpen => {
f.write_str("The bootloader wouldn't respond. Try again.")
},
TockloaderError::MalformedResponse(explanation) => {
f.write_str(format!("Received corrupted or unexpected response. {}", *explanation).as_str())
},
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/jlink/bootloader_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl BootloaderInterface for JLinkInterface {
todo!()
}

async fn get_attribute(&mut self) -> Result<Attribute, TockloaderError> {
async fn get_attribute(&mut self, _index: u8) -> Result<Attribute, TockloaderError> {
todo!()
}
}
2 changes: 1 addition & 1 deletion src/interfaces/openocd/bootloader_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl BootloaderInterface for OpenOCDInterface {
todo!()
}

async fn get_attribute(&mut self) -> Result<Attribute, TockloaderError> {
async fn get_attribute(&mut self, _index: u8) -> Result<Attribute, TockloaderError> {
todo!()
}
}
33 changes: 22 additions & 11 deletions src/interfaces/serial/bootloader_interface.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
use super::binary_codec::BinaryCodec;
use crate::{
bootloader::{
attribute::Attribute,
codes::{ESCAPE_CHAR, RESPONSE_PONG},
},
bootloader::{attribute::Attribute, codes::*},
errors::TockloaderError,
interfaces::traits::BootloaderInterface,
interfaces::SerialInterface,
timeout,
};
use async_trait::async_trait;
use futures::{SinkExt, StreamExt};
use futures::{SinkExt, StreamExt, TryFutureExt};
use std::time::Duration;
use tokio_serial::SerialPort;
use tokio_util::codec::Decoder;
Expand Down Expand Up @@ -99,9 +97,7 @@ impl BootloaderInterface for SerialInterface {

channel.send([ESCAPE_CHAR, 0x1]).await?;

if let Ok(response) =
tokio::time::timeout(Duration::from_millis(1000), channel.next()).await
{
if let Ok(response) = timeout!(channel.next()).await {
if let Some(decoder_result) = response {
let response = decoder_result?;
if response == [ESCAPE_CHAR, RESPONSE_PONG] {
Expand All @@ -117,10 +113,25 @@ impl BootloaderInterface for SerialInterface {
}

async fn sync(&mut self) -> Result<(), TockloaderError> {
todo!()
let mut channel = BinaryCodec.framed(self.stream.as_mut().unwrap());

channel.send([0x00, ESCAPE_CHAR, COMMAND_RESET]).await?;
Ok(())
}

async fn get_attribute(&mut self) -> Result<Attribute, TockloaderError> {
todo!()
async fn get_attribute(&mut self, index: u8) -> Result<Attribute, TockloaderError> {
self.sync().await?;

let mut channel = BinaryCodec.framed(self.stream.as_mut().unwrap());

channel
.send([index, ESCAPE_CHAR, COMMAND_GET_ATTRIBUTE])
.await?;
if let Some(decoder_result) = timeout!(channel.next()).await? {
return Attribute::parse_raw(decoder_result?);
}

// TODO: Is this the right error to give?
Err(TockloaderError::BootloaderNotOpen)
}
}
31 changes: 30 additions & 1 deletion src/interfaces/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,35 @@ pub trait VirtualTerminal {
async fn run_terminal(&mut self) -> Result<(), TockloaderError>;
}

/// This is a short-hand for tokio::time::timeout with a constant, pre-defined, timeout.
/// The macro also maps the [Elapsed](tokio::time::error::Elapsed) error to [Timeout](TockloaderError::Timeout).
/// Used mostly to timeout reading data from a board.
///
/// ## Expansion
/// ```
/// timeout!(channel.read())
/// ```
/// expands to
/// ```
/// tokio::time::timeout(Duration::from_millis(1000), channel.read()).map_err(|_| TockloaderError::Timeout)
/// ```
///
/// ## Example
/// ```
/// async fn read_data() -> Option(Vec<u8>);
/// // ...
/// if let Some(data) = timeout!(read_data()).await? {
/// println!("{}", data);
/// }
/// ```
#[macro_export]
macro_rules! timeout {
($operation:expr) => {
tokio::time::timeout(Duration::from_millis(1000), $operation)
.map_err(|_| TockloaderError::Timeout)
};
}

#[async_trait]
#[enum_dispatch]
pub trait BootloaderInterface {
Expand Down Expand Up @@ -44,5 +73,5 @@ pub trait BootloaderInterface {
async fn sync(&mut self) -> Result<(), TockloaderError>;

/// TODO! Description here, what exactly is an attribute?
async fn get_attribute(&mut self) -> Result<Attribute, TockloaderError>;
async fn get_attribute(&mut self, index: u8) -> Result<Attribute, TockloaderError>;
}
15 changes: 14 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ mod bootloader;
mod cli;
mod errors;
mod interfaces;
use std::io::{stdin, Read};

use cli::make_cli;
use errors::TockloaderError;
use interfaces::{build_interface, traits::*};
Expand Down Expand Up @@ -32,7 +34,18 @@ async fn run() -> Result<(), TockloaderError> {
Some(("info", sub_matches)) => {
let mut interface = build_interface(sub_matches)?;
interface.open()?;
dbg!(interface.enter_bootloader().await?);
if !interface.enter_bootloader().await? {
println!("Couldn't enter bootloader automatically. Please try entering it manually and press any key...");
// Read a single byte and discard
let _ = stdin().read(&mut [0u8]).unwrap();
if !interface.bootloader_open().await {
return Err(TockloaderError::BootloaderNotOpen);
}
}
for i in 0..16 {
let attribute = interface.get_attribute(i).await?;
println!("{}: {} = {:?}", i, attribute.key, attribute.value);
}
}
// If only the "--debug" flag is set, then this branch is executed
// Or, more likely at this stage, a subcommand hasn't been implemented yet.
Expand Down

0 comments on commit 204b897

Please sign in to comment.