From 9df0f3bd565dfbf8c97d02969a17504c688bf381 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 18 Feb 2019 21:36:31 +0000 Subject: Refactor command execution into commands module This patch refactors the command execution. A command is represented by a struct implementing the Command trait. The enum_cmd macro is used to generate a mapping from the CommandId enum to a Command instance and to execute the command. The request and response data is manually converted from and to raw byte slices. As we do not have a standard library, we cannot create a Box from a CommandId. Instead, we directly delegate the execute method to the corresponding Command. --- src/commands.rs | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 src/commands.rs (limited to 'src/commands.rs') diff --git a/src/commands.rs b/src/commands.rs new file mode 100644 index 0000000..62e2983 --- /dev/null +++ b/src/commands.rs @@ -0,0 +1,148 @@ +// Copyright 2019 Robin Krahl +// SPDX-License-Identifier: GPL-3.0-or-later + +use core::default::Default; +use core::marker::Sized; + +use crate::device::CommandStatus; + +pub const COMMAND_LEN: usize = 53; + +enum_cmd! { + #[derive(Clone, Copy, Debug, PartialEq)] + pub enum CommandId { + GetStatus(GetStatusCommand) = 0, + ReadSlotName(ReadSlotNameCommand) = 2, + } +} + +trait RequestData: Sized { + fn from_bytes(data: &[u8]) -> Option; +} + +impl RequestData for () { + fn from_bytes(_data: &[u8]) -> Option { + Some(()) + } +} + +trait ResponseData { + fn write_bytes(&self, buf: &mut [u8]); +} + +trait Command { + type Request: RequestData; + type Response: ResponseData; + + fn execute(data: Self::Request) -> Result; + + fn execute_raw(data: &[u8], buf: &mut [u8]) -> CommandStatus { + if let Some(request) = Self::Request::from_bytes(data) { + match Self::execute(request) { + Ok(response) => { + response.write_bytes(buf); + CommandStatus::Ok + } + Err(status) => status, + } + } else { + CommandStatus::NotSupported + } + } +} + +#[derive(Debug, Default)] +struct GetStatusResponse { + firmware_version_minor: u8, + firmware_version_major: u8, + card_serial: u32, + config_numlock: u8, + config_capslock: u8, + config_enable_user_password: u8, + config_delete_user_password: u8, + buf: [u8; 10], +} + +impl ResponseData for GetStatusResponse { + fn write_bytes(&self, buf: &mut [u8]) { + let card_serial = self.card_serial.to_le_bytes(); + let data = &[ + self.firmware_version_minor, + self.firmware_version_major, + card_serial[0], + card_serial[1], + card_serial[2], + card_serial[3], + self.config_numlock, + self.config_capslock, + self.config_enable_user_password, + self.config_delete_user_password, + ]; + assert!(buf.len() >= data.len()); + buf[..data.len()].copy_from_slice(data); + } +} + +#[derive(Debug, Default)] +struct GetStatusCommand {} + +impl Command for GetStatusCommand { + type Request = (); + type Response = GetStatusResponse; + + fn execute(_data: Self::Request) -> Result { + let mut response: Self::Response = Default::default(); + response.firmware_version_minor = 1; + Ok(response) + } +} + +#[derive(Debug, Default)] +struct ReadSlotNameRequest { + internal_slot_number: u8, +} + +impl RequestData for ReadSlotNameRequest { + fn from_bytes(data: &[u8]) -> Option { + if data.is_empty() { + None + } else { + Some(ReadSlotNameRequest { + internal_slot_number: data[0], + }) + } + } +} + +#[derive(Debug, Default)] +struct ReadSlotNameResponse { + slot_name: [u8; 15], +} + +impl ResponseData for ReadSlotNameResponse { + fn write_bytes(&self, buf: &mut [u8]) { + assert!(buf.len() >= self.slot_name.len()); + buf[..self.slot_name.len()].copy_from_slice(&self.slot_name) + } +} + +#[derive(Debug, Default)] +struct ReadSlotNameCommand {} + +impl Command for ReadSlotNameCommand { + type Request = ReadSlotNameRequest; + type Response = ReadSlotNameResponse; + + fn execute(data: Self::Request) -> Result { + if data.internal_slot_number != 0x20 { + Err(CommandStatus::SlotNotProgrammed) + } else { + let mut response: Self::Response = Default::default(); + response.slot_name[0] = 0x74; // t + response.slot_name[1] = 0x65; // e + response.slot_name[2] = 0x73; // s + response.slot_name[3] = 0x74; // t + Ok(response) + } + } +} -- cgit v1.2.1