Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: program firmware protocol #34

Merged
merged 8 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/encrypted/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ impl EncryptedCommand {
return;
}

#[cfg(not(test))]
let mut rng = rand::thread_rng();

let start = self.packing_start();
Expand Down
2 changes: 2 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub enum Error {
JsonRpc(String),
Event(String),
Enum(String),
Firmware(String),
}

impl fmt::Display for Error {
Expand Down Expand Up @@ -97,6 +98,7 @@ impl fmt::Display for Error {
Error::JsonRpc(err) => write!(f, "Failed processing JSON-RPC message(s): {err}"),
Error::Event(err) => write!(f, "Failed processing event message(s): {err}"),
Error::Enum(err) => write!(f, "Enum error: {err}"),
Error::Firmware(err) => write!(f, "Firmware error: {err}"),
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/firmware.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pub(crate) mod command;
pub(crate) mod header_command;
pub(crate) mod header_response;
pub(crate) mod response;

pub use command::*;
pub use header_command::*;
pub use header_response::*;
pub use response::*;
101 changes: 101 additions & 0 deletions src/firmware/command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use crate::{
impl_command_display, impl_command_ops, impl_default, impl_message_from_buf, impl_message_ops,
len::PROGRAM_FIRMWARE_COMMAND, CommandOps, MessageOps, MessageType,
};

mod index {
pub const FIRMWARE_CODE: usize = 4;
}

/// Represents the type of programming the unit expects.
// FIXME: there is currently only one code described in public documentation.
// Are there others still in use?
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ProgramFirmwareCode {
Ram = 0x03,
Reserved(u8),
}

impl From<u8> for ProgramFirmwareCode {
fn from(val: u8) -> Self {
match val {
0x03 => Self::Ram,
_ => Self::Reserved(val),
}
}
}

impl From<&ProgramFirmwareCode> for u8 {
fn from(val: &ProgramFirmwareCode) -> Self {
match val {
ProgramFirmwareCode::Ram => 0x03,
ProgramFirmwareCode::Reserved(code) => *code,
}
}
}

impl From<ProgramFirmwareCode> for u8 {
fn from(val: ProgramFirmwareCode) -> Self {
(&val).into()
}
}

/// ProgramFirmware - Command (0x0B)
///
/// This two byte command prepares the unit for firmware programming.
///
/// The `FirmwareCode` field defines the type of programming.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ProgramFirmwareCommand {
buf: [u8; PROGRAM_FIRMWARE_COMMAND],
}

impl ProgramFirmwareCommand {
/// Creates a new [SyncCommand] message.
pub fn new() -> Self {
let mut msg = Self {
buf: [0u8; PROGRAM_FIRMWARE_COMMAND],
};

msg.init();
msg.set_command(MessageType::ProgramFirmware);
msg.set_firmware_code(ProgramFirmwareCode::Ram);

msg
}

/// Gets the [FirmwareCode] for the type of programming the unit expects.
///
/// Example:
///
/// ```
/// # use ssp;
/// let fw_cmd = ssp::ProgramFirmwareCommand::new();
/// assert_eq!(fw_cmd.firmware_code(), ssp::ProgramFirmwareCode::Ram);
/// ```
pub fn firmware_code(&self) -> ProgramFirmwareCode {
self.buf[index::FIRMWARE_CODE].into()
}

/// Sets the [FirmwareCode] for the type of programming the unit expects.
///
/// Example:
///
/// ```
/// # use ssp;
/// let mut fw_cmd = ssp::ProgramFirmwareCommand::new();
/// fw_cmd.set_firmware_code(ssp::ProgramFirmwareCode::Ram);
/// assert_eq!(fw_cmd.firmware_code(), ssp::ProgramFirmwareCode::Ram);
/// ```
pub fn set_firmware_code(&mut self, code: ProgramFirmwareCode) {
self.buf[index::FIRMWARE_CODE] = code.into();
}
}

impl_default!(ProgramFirmwareCommand);
impl_command_display!(ProgramFirmwareCommand);
impl_message_from_buf!(ProgramFirmwareCommand);
impl_message_ops!(ProgramFirmwareCommand);
impl_command_ops!(ProgramFirmwareCommand);
80 changes: 80 additions & 0 deletions src/firmware/header_command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use crate::{
impl_command_display, impl_command_ops, impl_default, impl_message_from_buf, impl_message_ops,
len::FIRMWARE_HEADER_COMMAND, CommandOps, FirmwareHeader, MessageOps, Result,
};

mod index {
pub const FIRMWARE_HEADER: usize = 3;
pub const FIRMWARE_HEADER_END: usize = 131;
}

/// ProgramFirmware - Command (0x0B)
///
/// This two byte command prepares the unit for firmware programming.
///
/// The `FirmwareCode` field defines the type of programming.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FirmwareHeaderCommand {
buf: [u8; FIRMWARE_HEADER_COMMAND],
}

impl FirmwareHeaderCommand {
/// Creates a new [FirmwareHeaderCommand] message.
pub fn new() -> Self {
let mut msg = Self {
buf: [0u8; FIRMWARE_HEADER_COMMAND],
};

msg.init();
msg.set_firmware_header(&FirmwareHeader::new()).ok();

msg
}

/// Creates a new [FirmwareHeaderCommand] message.
pub fn create(header: &FirmwareHeader) -> Result<Self> {
let mut msg = Self {
buf: [0u8; FIRMWARE_HEADER_COMMAND],
};

msg.init();
msg.set_firmware_header(header)?;

Ok(msg)
}

/// Gets the [FirmwareCode] for the type of programming the unit expects.
///
/// Example:
///
/// ```
/// # use ssp;
/// let fw_cmd = ssp::FirmwareHeaderCommand::new();
/// assert_eq!(fw_cmd.firmware_header().unwrap(), ssp::FirmwareHeader::new());
/// ```
pub fn firmware_header(&self) -> Result<FirmwareHeader> {
FirmwareHeader::try_from(&self.buf[index::FIRMWARE_HEADER..index::FIRMWARE_HEADER_END])
}

/// Sets the [FirmwareHeader] for the type of programming the unit expects.
///
/// Example:
///
/// ```
/// # use ssp;
/// let mut fw_cmd = ssp::FirmwareHeaderCommand::new();
/// let header = ssp::FirmwareHeader::new();
/// fw_cmd.set_firmware_header(&header);
/// assert_eq!(fw_cmd.firmware_header().unwrap(), header);
/// ```
pub fn set_firmware_header(&mut self, header: &FirmwareHeader) -> Result<()> {
header.to_bytes(&mut self.buf[index::FIRMWARE_HEADER..index::FIRMWARE_HEADER_END])
}
}

impl_default!(FirmwareHeaderCommand);
impl_command_display!(FirmwareHeaderCommand);
impl_message_from_buf!(FirmwareHeaderCommand);
impl_message_ops!(FirmwareHeaderCommand);
impl_command_ops!(FirmwareHeaderCommand);
32 changes: 32 additions & 0 deletions src/firmware/header_response.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use crate::{
impl_default, impl_message_from_buf, impl_message_ops, impl_response_display,
impl_response_ops, len::FIRMWARE_HEADER_RESPONSE, message::MessageOps, MessageType,
};

/// FirmwareHeader - Response (0x0B)
///
/// Represents a response to an [FirmwareHeaderCommand](crate::FirmwareHeaderCommand) message.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FirmwareHeaderResponse {
buf: [u8; FIRMWARE_HEADER_RESPONSE],
}

impl FirmwareHeaderResponse {
/// Creates a new [ProgramFirmwareResponse] message.
pub fn new() -> Self {
let mut msg = Self {
buf: [0u8; FIRMWARE_HEADER_RESPONSE],
};

msg.init();

msg
}
}

impl_default!(FirmwareHeaderResponse);
impl_message_from_buf!(FirmwareHeaderResponse);
impl_message_ops!(FirmwareHeaderResponse, MessageType::ProgramFirmware);
impl_response_ops!(FirmwareHeaderResponse);
impl_response_display!(FirmwareHeaderResponse);
64 changes: 64 additions & 0 deletions src/firmware/response.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use crate::{
impl_default, impl_message_from_buf, impl_message_ops, impl_response_display,
impl_response_ops, len::PROGRAM_FIRMWARE_RESPONSE, message::MessageOps, MessageType,
};

mod index {
pub const BLOCK_LEN: usize = 4;
pub const BLOCK_LEN_END: usize = 5;
}

/// ProgramFirmware - Response (0x0B)
///
/// Represents a response to an [ProgramFirmwareCommand](crate::ProgramFirmwareCommand) message.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ProgramFirmwareResponse {
buf: [u8; PROGRAM_FIRMWARE_RESPONSE],
}

impl ProgramFirmwareResponse {
/// Creates a new [ProgramFirmwareResponse] message.
pub fn new() -> Self {
let mut msg = Self {
buf: [0u8; PROGRAM_FIRMWARE_RESPONSE],
};

msg.init();

msg
}

/// Gets the expected block length from the device.
///
/// Example:
///
/// ```
/// # use ssp;
/// let fw_res = ssp::ProgramFirmwareResponse::new();
/// assert_eq!(fw_res.block_len(), 0);
/// ```
pub fn block_len(&self) -> u16 {
u16::from_le_bytes([self.buf[index::BLOCK_LEN], self.buf[index::BLOCK_LEN_END]])
}

/// Sets the expected block length from the device.
///
/// Example:
///
/// ```
/// # use ssp;
/// let mut fw_res = ssp::ProgramFirmwareResponse::new();
/// fw_res.set_block_len(128);
/// assert_eq!(fw_res.block_len(), 128);
/// ```
pub fn set_block_len(&mut self, len: u16) {
self.buf[index::BLOCK_LEN..=index::BLOCK_LEN_END].copy_from_slice(&len.to_le_bytes());
}
}

impl_default!(ProgramFirmwareResponse);
impl_message_from_buf!(ProgramFirmwareResponse);
impl_message_ops!(ProgramFirmwareResponse, MessageType::ProgramFirmware);
impl_response_ops!(ProgramFirmwareResponse);
impl_response_display!(ProgramFirmwareResponse);
8 changes: 8 additions & 0 deletions src/len.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ pub const ENABLE_PAYOUT_RESPONSE: usize = 6;
pub const DISABLE_PAYOUT_COMMAND: usize = 6;
/// DisablePayout Response full message length.
pub const DISABLE_PAYOUT_RESPONSE: usize = 6;
/// ProgramFirmware Command full message length.
pub const PROGRAM_FIRMWARE_COMMAND: usize = 7;
/// ProgramFirmware Response full message length.
pub const PROGRAM_FIRMWARE_RESPONSE: usize = 8;
/// FirmwareHeader Command full message length.
pub const FIRMWARE_HEADER_COMMAND: usize = 133;
/// FirmwareHeader Response full message length.
pub const FIRMWARE_HEADER_RESPONSE: usize = 6;
/// Encrypted Command full message length.
///
/// Because encrypted messages have variable lengths, set the static length to maximum
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub mod encrypted;
pub mod encryption_reset;
pub mod error;
pub mod event_ack;
pub mod firmware;
pub mod get_barcode_data;
pub mod get_barcode_inhibit;
pub mod get_barcode_reader_configuration;
Expand Down Expand Up @@ -93,6 +94,7 @@ pub use encrypted::*;
pub use encryption_reset::*;
pub use error::*;
pub use event_ack::*;
pub use firmware::*;
pub use get_barcode_data::*;
pub use get_barcode_inhibit::*;
pub use get_barcode_reader_configuration::*;
Expand Down
2 changes: 2 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub(crate) mod country_code;
pub(crate) mod device_status;
pub(crate) mod encryption;
pub(crate) mod events;
pub(crate) mod firmware;
pub(crate) mod inhibit;
pub(crate) mod last_reject_code;
pub(crate) mod message_type;
Expand All @@ -26,6 +27,7 @@ pub use country_code::*;
pub use device_status::*;
pub use encryption::*;
pub use events::*;
pub use firmware::*;
pub use inhibit::*;
pub use last_reject_code::*;
pub use message_type::*;
Expand Down
Loading
Loading