rxprog/command/
command.rs

1use std::fmt;
2use std::io;
3use std::num::Wrapping;
4
5use crate::Result;
6
7/// A command which can be sent to a device, and results in either a response or error
8pub trait Command {
9    /// Result of a successful command execution
10    type Response;
11
12    /// Executes the command on a device
13    fn execute<T: io::Read + io::Write>(&self, p: &mut T) -> Result<Self::Response>;
14}
15
16pub trait Transmit {
17    fn tx<T: io::Write>(&self, p: &mut T) -> Result<()>;
18}
19
20pub struct CommandData {
21    pub opcode: u8,
22    pub has_size_field: bool,
23    pub payload: Vec<u8>,
24}
25
26impl CommandData {
27    fn bytes(&self) -> Vec<u8> {
28        let mut bytes = vec![];
29        let payload = &self.payload;
30        let payload_size = payload.len();
31
32        bytes.push(self.opcode);
33
34        if self.has_size_field {
35            bytes.push(payload_size as u8);
36        }
37
38        bytes.extend(payload);
39
40        if payload_size != 0 {
41            let sum = bytes.iter().map(|x| Wrapping(*x)).sum::<Wrapping<u8>>();
42            let checksum = !sum + Wrapping::<u8>(1);
43            bytes.push(checksum.0);
44        }
45
46        bytes
47    }
48}
49
50pub trait TransmitCommandData {
51    fn command_data(&self) -> CommandData;
52}
53
54impl<T: TransmitCommandData> Transmit for T {
55    fn tx<U: io::Write>(&self, p: &mut U) -> Result<()> {
56        p.write(&self.command_data().bytes())?;
57        p.flush()?;
58
59        Ok(())
60    }
61}
62
63pub trait Receive {
64    type Response;
65
66    fn rx<T: io::Read>(&self, p: &mut T) -> Result<Self::Response>;
67}
68
69impl<T: Transmit + Receive> Command for T {
70    type Response = T::Response;
71
72    fn execute<U: io::Read + io::Write>(&self, p: &mut U) -> Result<Self::Response> {
73        self.tx(p)?;
74        self.rx(p)
75    }
76}
77
78/// An error returned by a target in response to a command
79#[derive(Copy, Clone, Debug, PartialEq)]
80pub enum CommandError {
81    /// Invalid address or area
82    Address,
83    /// Requested bit rate could not be selected within an acceptable margin of
84    /// error
85    BitRateSelection,
86    /// Invalid block number
87    BlockNumber,
88    /// Checksum mismatch
89    Checksum,
90    /// Invalid clock mode
91    ClockMode,
92    /// Invalid data size (zero, too large, or calculated end address out of
93    /// bounds)
94    DataSize,
95    /// Invalid device code
96    DeviceCode,
97    /// Error occurred during erasure (target or user initiated)
98    Erasure,
99    /// Supplied ID code did not match
100    IDCodeMismatch,
101    /// Input frequency out of range for selected clock mode
102    InputFrequency,
103    /// Multiplication ratio invalid for selected clock mode
104    MultiplicationRatio,
105    /// Calculated operating frequency out of range for clock
106    OperatingFrequency,
107    /// Error occurred during programming
108    Programming,
109    /// Failed to transition into programming/erasure state
110    ProgrammingErasureStateTransition,
111}
112
113impl fmt::Display for CommandError {
114    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115        write!(
116            f,
117            "{}",
118            match self {
119                CommandError::Address => "invalid address/area",
120                CommandError::BitRateSelection => "bit rate selection error too high",
121                CommandError::BlockNumber => "invalid block number",
122                CommandError::Checksum => "checksum mismatch",
123                CommandError::ClockMode => "invalid clock mode",
124                CommandError::DataSize => "invalid data size",
125                CommandError::DeviceCode => "invalid device code",
126                CommandError::Erasure => "erasure error",
127                CommandError::IDCodeMismatch => "ID code mismatch",
128                CommandError::InputFrequency => "input frequency out of range",
129                CommandError::MultiplicationRatio => "invalid multiplication ratio",
130                CommandError::OperatingFrequency => "calculated operating frequency out of range",
131                CommandError::Programming => "programming error",
132                CommandError::ProgrammingErasureStateTransition => {
133                    "failed to transition into programming/erasure state"
134                }
135            }
136        )
137    }
138}