aboutsummaryrefslogtreecommitdiff
path: root/src/commands.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/commands.rs')
-rw-r--r--src/commands.rs148
1 files changed, 148 insertions, 0 deletions
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 <robin.krahl@ireas.org>
+// 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<Self>;
+}
+
+impl RequestData for () {
+ fn from_bytes(_data: &[u8]) -> Option<Self> {
+ Some(())
+ }
+}
+
+trait ResponseData {
+ fn write_bytes(&self, buf: &mut [u8]);
+}
+
+trait Command {
+ type Request: RequestData;
+ type Response: ResponseData;
+
+ fn execute(data: Self::Request) -> Result<Self::Response, CommandStatus>;
+
+ 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<Self::Response, CommandStatus> {
+ 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<Self> {
+ 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<Self::Response, CommandStatus> {
+ 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)
+ }
+ }
+}