Skip to content

Commit

Permalink
clean up api and error types
Browse files Browse the repository at this point in the history
  • Loading branch information
pd0wm committed Mar 20, 2024
1 parent 0bba611 commit 467a941
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 124 deletions.
4 changes: 2 additions & 2 deletions examples/query_fw_versions.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use automotive::can::AsyncCanAdapter;
use automotive::can::Identifier;
use automotive::error::Error;
use automotive::isotp::{IsoTPAdapter, IsoTPConfig};
use automotive::Error;

use automotive::uds::constants::DataIdentifier;
use automotive::uds::DataIdentifier;
use automotive::uds::UDSClient;

use bstr::ByteSlice;
Expand Down
2 changes: 1 addition & 1 deletion examples/uds.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use automotive::uds::constants::{DataIdentifier, SessionType};
use automotive::uds::{DataIdentifier, SessionType};
use bstr::ByteSlice;

#[tokio::main]
Expand Down
6 changes: 3 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ pub enum Error {
#[error("Timeout")]
Timeout,
#[error(transparent)]
IsoTPError(#[from] crate::isotp::error::Error),
IsoTPError(#[from] crate::isotp::Error),
#[error(transparent)]
LibUsbError(#[from] rusb::Error),
#[error(transparent)]
PandaError(#[from] crate::panda::error::Error),
PandaError(#[from] crate::panda::Error),
#[error(transparent)]
UDSError(#[from] crate::uds::error::Error),
UDSError(#[from] crate::uds::Error),
}

impl From<tokio_stream::Elapsed> for Error {
Expand Down
47 changes: 23 additions & 24 deletions src/isotp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,16 @@
//! }
//! ```

pub mod constants;
pub mod error;
pub mod types;
mod constants;
mod error;
mod types;

pub use constants::{FlowStatus, FrameType, FLOW_SATUS_MASK, FRAME_TYPE_MASK};
pub use error::Error;

use crate::can::AsyncCanAdapter;
use crate::can::{Frame, Identifier, DLC_TO_LEN};
use crate::error::Error;
use crate::isotp::constants::FlowStatus;
use crate::isotp::constants::FLOW_SATUS_MASK;
use crate::isotp::constants::{FrameType, FRAME_TYPE_MASK};

use crate::Result;
use async_stream::stream;
use futures_core::stream::Stream;
use tokio_stream::{StreamExt, Timeout};
Expand Down Expand Up @@ -175,7 +174,7 @@ impl<'a> IsoTPAdapter<'a> {
}

/// Build a CAN frame from the payload. Inserts extended address and padding if needed.
fn frame(&self, data: &[u8]) -> Result<Frame, Error> {
fn frame(&self, data: &[u8]) -> Result<Frame> {
let mut data = data.to_vec();

if let Some(ext_address) = self.config.ext_address {
Expand All @@ -185,7 +184,7 @@ impl<'a> IsoTPAdapter<'a> {
// Check if the data length is valid
if !DLC_TO_LEN.contains(&data.len()) {
println!("len {}", data.len());
return Err(crate::error::Error::MalformedFrame);
return Err(crate::Error::MalformedFrame);
}

let frame = Frame {
Expand All @@ -199,7 +198,7 @@ impl<'a> IsoTPAdapter<'a> {
Ok(frame)
}

pub async fn send_single_frame(&self, data: &[u8]) -> Result<(), Error> {
pub async fn send_single_frame(&self, data: &[u8]) -> Result<()> {
let mut buf;

if data.len() < 0xf {
Expand All @@ -220,7 +219,7 @@ impl<'a> IsoTPAdapter<'a> {
Ok(())
}

pub async fn send_first_frame(&self, data: &[u8]) -> Result<usize, Error> {
pub async fn send_first_frame(&self, data: &[u8]) -> Result<usize> {
let mut buf;
if data.len() <= ISO_TP_MAX_DLEN {
let b0: u8 = FrameType::First as u8 | ((data.len() >> 8) & 0xF) as u8;
Expand All @@ -242,7 +241,7 @@ impl<'a> IsoTPAdapter<'a> {
Ok(offset)
}

pub async fn send_consecutive_frame(&self, data: &[u8], idx: usize) -> Result<(), Error> {
pub async fn send_consecutive_frame(&self, data: &[u8], idx: usize) -> Result<()> {
let idx = ((idx + 1) & 0xF) as u8;

let mut buf = vec![FrameType::Consecutive as u8 | idx];
Expand All @@ -261,7 +260,7 @@ impl<'a> IsoTPAdapter<'a> {
async fn receive_flow_control(
&self,
stream: &mut std::pin::Pin<&mut Timeout<impl Stream<Item = Frame>>>,
) -> Result<FlowControlConfig, Error> {
) -> Result<FlowControlConfig> {
for _ in 0..MAX_WAIT_FC {
let mut frame = stream.next().await.unwrap()?;

Expand Down Expand Up @@ -296,7 +295,7 @@ impl<'a> IsoTPAdapter<'a> {
Err(crate::isotp::error::Error::TooManyFCWait.into())
}

async fn send_multiple(&self, data: &[u8]) -> Result<(), Error> {
async fn send_multiple(&self, data: &[u8]) -> Result<()> {
// Stream for receiving flow control
let stream = self
.adapter
Expand Down Expand Up @@ -346,7 +345,7 @@ impl<'a> IsoTPAdapter<'a> {
}

/// Asynchronously send an ISO-TP frame of up to 4095 bytes. Returns [`Error::Timeout`] if the ECU is not responding in time with flow control messages.
pub async fn send(&self, data: &[u8]) -> Result<(), Error> {
pub async fn send(&self, data: &[u8]) -> Result<()> {
debug!("TX {}", hex::encode(data));

// Single frame has 1 byte of overhead for CAN, and 2 bytes for CAN-FD with escape sequence
Expand All @@ -364,7 +363,7 @@ impl<'a> IsoTPAdapter<'a> {
Ok(())
}

async fn recv_single_frame(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
async fn recv_single_frame(&self, data: &[u8]) -> Result<Vec<u8>> {
let mut len = (data[0] & 0xF) as usize;
let mut offset = 1;

Expand All @@ -384,7 +383,7 @@ impl<'a> IsoTPAdapter<'a> {
Ok(data[offset..len + offset].to_vec())
}

async fn recv_first_frame(&self, data: &[u8], buf: &mut Vec<u8>) -> Result<usize, Error> {
async fn recv_first_frame(&self, data: &[u8], buf: &mut Vec<u8>) -> Result<usize> {
let b0 = data[0] as u16;
let b1 = data[1] as u16;
let mut len = ((b0 << 8 | b1) & 0xFFF) as usize;
Expand Down Expand Up @@ -422,7 +421,7 @@ impl<'a> IsoTPAdapter<'a> {
buf: &mut Vec<u8>,
len: usize,
idx: u8,
) -> Result<u8, Error> {
) -> Result<u8> {
let msg_idx = data[0] & 0xF;
let remaining_len = len - buf.len();

Expand Down Expand Up @@ -462,7 +461,7 @@ impl<'a> IsoTPAdapter<'a> {
async fn recv_from_stream(
&self,
stream: &mut std::pin::Pin<&mut Timeout<impl Stream<Item = Frame>>>,
) -> Result<Vec<u8>, Error> {
) -> Result<Vec<u8>> {
let mut buf = Vec::new();
let mut len: Option<usize> = None;
let mut idx: u8 = 1;
Expand All @@ -478,7 +477,7 @@ impl<'a> IsoTPAdapter<'a> {
Some(FrameType::First) => {
// If we already received a first frame, something went wrong
if len.is_some() {
return Err(Error::IsoTPError(crate::isotp::error::Error::OutOfOrder));
return Err(Error::OutOfOrder.into());
}
len = Some(self.recv_first_frame(data, &mut buf).await?);
}
Expand All @@ -491,20 +490,20 @@ impl<'a> IsoTPAdapter<'a> {
return Ok(buf);
}
} else {
return Err(Error::IsoTPError(crate::isotp::error::Error::OutOfOrder));
return Err(Error::OutOfOrder.into());
}
}
Some(FrameType::FlowControl) => {} // Ignore flow control frames, these are from a simultaneous transmission
_ => {
return Err(crate::isotp::error::Error::UnknownFrameType.into());
return Err(Error::UnknownFrameType.into());
}
};
}
unreachable!();
}

/// Stream of ISO-TP packets. Can be used if multiple responses are expected from a single request. Returns [`Error::Timeout`] if the timeout is exceeded between individual ISO-TP frames. Note the total time to receive a packet may be longer than the timeout.
pub fn recv(&self) -> impl Stream<Item = Result<Vec<u8>, Error>> + '_ {
pub fn recv(&self) -> impl Stream<Item = Result<Vec<u8>>> + '_ {
let stream = self
.adapter
.recv_filter(|frame| {
Expand Down
7 changes: 5 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
//! let uds = automotive::uds::UDSClient::new(&isotp);
//!
//! uds.tester_present().await.unwrap();
//! let response = uds.read_data_by_identifier(automotive::uds::constants::DataIdentifier::ApplicationSoftwareIdentification as u16).await.unwrap();
//! let response = uds.read_data_by_identifier(automotive::uds::DataIdentifier::ApplicationSoftwareIdentification as u16).await.unwrap();
//!
//! println!("Application Software Identification: {}", hex::encode(response));
//! }
Expand All @@ -41,10 +41,13 @@
//!

pub mod can;
pub mod error;
mod error;
pub mod isotp;
pub mod panda;
pub mod uds;

pub use error::Error;
pub type Result<T> = std::result::Result<T, Error>;

#[cfg(target_os = "linux")]
pub mod socketcan;
40 changes: 19 additions & 21 deletions src/panda/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
//! Panda CAN adapter support

mod constants;
pub mod error;
mod error;
mod usb_protocol;

pub use error::Error;
use std::vec;

use crate::can::AsyncCanAdapter;
use crate::can::CanAdapter;
use crate::error::Error;
use crate::panda::constants::{Endpoint, HwType, SafetyModel};
use crate::Result;
use tracing::{info, warn};

const VENDOR_ID: u16 = 0xbbaa;
Expand All @@ -35,13 +36,13 @@ unsafe impl Send for Panda {}

impl Panda {
/// Convenience function to create a new panda adapter and wrap in an [`AsyncCanAdapter`]
pub fn new_async() -> Result<AsyncCanAdapter, Error> {
pub fn new_async() -> Result<AsyncCanAdapter> {
let panda = Panda::new()?;
Ok(AsyncCanAdapter::new(panda))
}

/// Connect to the first available panda. This function will set the safety mode to ALL_OUTPUT and clear all buffers.
pub fn new() -> Result<Panda, Error> {
pub fn new() -> Result<Panda> {
for device in rusb::devices().unwrap().iter() {
let device_desc = device.device_descriptor().unwrap();

Expand All @@ -63,9 +64,7 @@ impl Panda {
// Check panda firmware version
let versions = panda.get_packets_versions()?;
if versions.can_version != EXPECTED_CAN_PACKET_VERSION {
return Err(Error::PandaError(
crate::panda::error::Error::WrongFirmwareVersion,
));
return Err(Error::WrongFirmwareVersion.into());
}

panda.set_safety_model(SafetyModel::AllOutput)?;
Expand All @@ -81,10 +80,10 @@ impl Panda {

return Ok(panda);
}
Err(Error::NotFound)
Err(crate::Error::NotFound)
}

fn flush_rx(&self) -> Result<(), Error> {
fn flush_rx(&self) -> Result<()> {
const N: usize = 16384;
let mut buf: [u8; N] = [0; N];

Expand All @@ -100,27 +99,26 @@ impl Panda {
}

/// Change the safety model of the panda. This can be useful to switch to Silent mode or open/close the relay in the comma.ai harness
pub fn set_safety_model(&self, safety_model: SafetyModel) -> Result<(), Error> {
pub fn set_safety_model(&self, safety_model: SafetyModel) -> Result<()> {
let safety_param: u16 = 0;
self.usb_write_control(Endpoint::SafetyModel, safety_model as u16, safety_param)
}

fn set_heartbeat_disabled(&self) -> Result<(), Error> {
fn set_heartbeat_disabled(&self) -> Result<()> {
self.usb_write_control(Endpoint::HeartbeatDisabled, 0, 0)
}

fn set_power_save(&self, power_save_enabled: bool) -> Result<(), Error> {
fn set_power_save(&self, power_save_enabled: bool) -> Result<()> {
self.usb_write_control(Endpoint::PowerSave, power_save_enabled as u16, 0)
}

/// Get the hardware type of the panda. Usefull to detect if it supports CAN-FD.
pub fn get_hw_type(&self) -> Result<HwType, Error> {
pub fn get_hw_type(&self) -> Result<HwType> {
let hw_type = self.usb_read_control(Endpoint::HwType, 1)?;
HwType::from_repr(hw_type[0])
.ok_or(Error::PandaError(crate::panda::error::Error::UnknownHwType))
HwType::from_repr(hw_type[0]).ok_or(Error::UnknownHwType.into())
}

fn get_packets_versions(&self) -> Result<Versions, Error> {
fn get_packets_versions(&self) -> Result<Versions> {
let versions = self.usb_read_control(Endpoint::PacketsVersions, 3)?;
Ok({
Versions {
Expand All @@ -131,11 +129,11 @@ impl Panda {
})
}

fn can_reset_communications(&self) -> Result<(), Error> {
fn can_reset_communications(&self) -> Result<()> {
self.usb_write_control(Endpoint::CanResetCommunications, 0, 0)
}

fn usb_read_control(&self, endpoint: Endpoint, n: usize) -> Result<Vec<u8>, Error> {
fn usb_read_control(&self, endpoint: Endpoint, n: usize) -> Result<Vec<u8>> {
let mut buf: Vec<u8> = vec![0; n];

let request_type = rusb::request_type(
Expand All @@ -150,7 +148,7 @@ impl Panda {
Ok(buf)
}

fn usb_write_control(&self, endpoint: Endpoint, value: u16, index: u16) -> Result<(), Error> {
fn usb_write_control(&self, endpoint: Endpoint, value: u16, index: u16) -> Result<()> {
let request_type = rusb::request_type(
rusb::Direction::Out,
rusb::RequestType::Standard,
Expand All @@ -170,7 +168,7 @@ impl Panda {

impl CanAdapter for Panda {
/// Sends a buffer of CAN messages to the panda.
fn send(&mut self, frames: &[crate::can::Frame]) -> Result<(), Error> {
fn send(&mut self, frames: &[crate::can::Frame]) -> Result<()> {
if frames.is_empty() {
return Ok(());
}
Expand All @@ -185,7 +183,7 @@ impl CanAdapter for Panda {
}

/// Reads the current buffer of available CAN messages from the panda. This function will return an empty vector if no messages are available. In case of a recoverable error (e.g. unpacking error), the buffer will be cleared and an empty vector will be returned.
fn recv(&mut self) -> Result<Vec<crate::can::Frame>, Error> {
fn recv(&mut self) -> Result<Vec<crate::can::Frame>> {
let mut buf: [u8; MAX_BULK_SIZE] = [0; MAX_BULK_SIZE];

let recv: usize = self
Expand Down
Loading

0 comments on commit 467a941

Please sign in to comment.