pub const APDU_TAG_PREFIX: u8 = 0x9F;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct ApduTag(u32);
impl ApduTag {
#[must_use]
pub const fn from_bytes(b1: u8, b2: u8, b3: u8) -> Self {
Self(((b1 as u32) << 16) | ((b2 as u32) << 8) | b3 as u32)
}
#[must_use]
pub const fn from_u24(v: u32) -> Self {
Self(v & 0x00FF_FFFF)
}
#[must_use]
pub const fn as_u24(self) -> u32 {
self.0
}
#[must_use]
pub const fn to_bytes(self) -> [u8; 3] {
[(self.0 >> 16) as u8, (self.0 >> 8) as u8, self.0 as u8]
}
#[must_use]
pub fn name(self) -> &'static str {
match self {
PROFILE_ENQ => "profile_enq",
PROFILE => "profile",
PROFILE_CHANGE => "profile_change",
APPLICATION_INFO_ENQ => "application_info_enq",
APPLICATION_INFO => "application_info",
ENTER_MENU => "enter_menu",
CA_INFO_ENQ => "ca_info_enq",
CA_INFO => "ca_info",
CA_PMT => "ca_pmt",
CA_PMT_REPLY => "ca_pmt_reply",
DATE_TIME_ENQ => "date_time_enq",
DATE_TIME => "date_time",
CLOSE_MMI => "close_mmi",
TUNE => "tune",
REPLACE => "replace",
CLEAR_REPLACE => "clear_replace",
ASK_RELEASE => "ask_release",
DISPLAY_CONTROL => "display_control",
DISPLAY_REPLY => "display_reply",
TEXT_LAST => "text_last",
TEXT_MORE => "text_more",
KEYPAD_CONTROL => "keypad_control",
KEYPRESS => "keypress",
ENQ => "enq",
ANSW => "answ",
MENU_LAST => "menu_last",
MENU_MORE => "menu_more",
MENU_ANSW => "menu_answ",
LIST_LAST => "list_last",
LIST_MORE => "list_more",
SUBTITLE_SEGMENT_LAST => "subtitle_segment_last",
SUBTITLE_SEGMENT_MORE => "subtitle_segment_more",
DISPLAY_MESSAGE => "display_message",
SCENE_END_MARK => "scene_end_mark",
SCENE_DONE => "scene_done",
SCENE_CONTROL => "scene_control",
SUBTITLE_DOWNLOAD_LAST => "subtitle_download_last",
SUBTITLE_DOWNLOAD_MORE => "subtitle_download_more",
FLUSH_DOWNLOAD => "flush_download",
DOWNLOAD_REPLY => "download_reply",
COMMS_CMD => "comms_cmd",
CONNECTION_DESCRIPTOR => "connection_descriptor",
COMMS_REPLY => "comms_reply",
COMMS_SEND_LAST => "comms_send_last",
COMMS_SEND_MORE => "comms_send_more",
COMMS_RCV_LAST => "comms_rcv_last",
COMMS_RCV_MORE => "comms_rcv_more",
_ => "unknown",
}
}
}
impl core::fmt::Display for ApduTag {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let [a, b, c] = self.to_bytes();
match self.name() {
"unknown" => write!(f, "apdu_tag(0x{a:02X}{b:02X}{c:02X})"),
n => write!(f, "{n}(0x{a:02X}{b:02X}{c:02X})"),
}
}
}
pub const PROFILE_ENQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x10);
pub const PROFILE: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x11);
pub const PROFILE_CHANGE: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x12);
pub const APPLICATION_INFO_ENQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x20);
pub const APPLICATION_INFO: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x21);
pub const ENTER_MENU: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x22);
pub const CA_INFO_ENQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x30);
pub const CA_INFO: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x31);
pub const CA_PMT: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x32);
pub const CA_PMT_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x33);
pub const DATE_TIME_ENQ: ApduTag = ApduTag::from_bytes(0x9F, 0x84, 0x40);
pub const DATE_TIME: ApduTag = ApduTag::from_bytes(0x9F, 0x84, 0x41);
pub const TUNE: ApduTag = ApduTag::from_bytes(0x9F, 0x84, 0x00);
pub const REPLACE: ApduTag = ApduTag::from_bytes(0x9F, 0x84, 0x01);
pub const CLEAR_REPLACE: ApduTag = ApduTag::from_bytes(0x9F, 0x84, 0x02);
pub const ASK_RELEASE: ApduTag = ApduTag::from_bytes(0x9F, 0x84, 0x03);
pub const CLOSE_MMI: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x00);
pub const DISPLAY_CONTROL: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x01);
pub const DISPLAY_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x02);
pub const TEXT_LAST: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x03);
pub const TEXT_MORE: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x04);
pub const KEYPAD_CONTROL: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x05);
pub const KEYPRESS: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x06);
pub const ENQ: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x07);
pub const ANSW: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x08);
pub const MENU_LAST: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x09);
pub const MENU_MORE: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x0A);
pub const MENU_ANSW: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x0B);
pub const LIST_LAST: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x0C);
pub const LIST_MORE: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x0D);
pub const SUBTITLE_SEGMENT_LAST: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x0E);
pub const SUBTITLE_SEGMENT_MORE: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x0F);
pub const DISPLAY_MESSAGE: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x10);
pub const SCENE_END_MARK: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x11);
pub const SCENE_DONE: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x12);
pub const SCENE_CONTROL: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x13);
pub const SUBTITLE_DOWNLOAD_LAST: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x14);
pub const SUBTITLE_DOWNLOAD_MORE: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x15);
pub const FLUSH_DOWNLOAD: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x16);
pub const DOWNLOAD_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0x88, 0x17);
pub const COMMS_CMD: ApduTag = ApduTag::from_bytes(0x9F, 0x8C, 0x00);
pub const CONNECTION_DESCRIPTOR: ApduTag = ApduTag::from_bytes(0x9F, 0x8C, 0x01);
pub const COMMS_REPLY: ApduTag = ApduTag::from_bytes(0x9F, 0x8C, 0x02);
pub const COMMS_SEND_LAST: ApduTag = ApduTag::from_bytes(0x9F, 0x8C, 0x03);
pub const COMMS_SEND_MORE: ApduTag = ApduTag::from_bytes(0x9F, 0x8C, 0x04);
pub const COMMS_RCV_LAST: ApduTag = ApduTag::from_bytes(0x9F, 0x8C, 0x05);
pub const COMMS_RCV_MORE: ApduTag = ApduTag::from_bytes(0x9F, 0x8C, 0x06);
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
#[test]
fn byte_round_trip() {
let t = ApduTag::from_bytes(0x9F, 0x80, 0x32);
assert_eq!(t.to_bytes(), [0x9F, 0x80, 0x32]);
assert_eq!(t.as_u24(), 0x009F_8032);
assert_eq!(ApduTag::from_u24(0x9F_8032), t);
assert_eq!(t, CA_PMT);
}
#[test]
fn names_match_table_58() {
assert_eq!(CA_PMT.name(), "ca_pmt");
assert_eq!(CA_PMT_REPLY.name(), "ca_pmt_reply");
assert_eq!(PROFILE_ENQ.name(), "profile_enq");
assert_eq!(DATE_TIME.name(), "date_time");
assert_eq!(ApduTag::from_bytes(0x9F, 0x99, 0x99).name(), "unknown");
}
#[test]
fn display_is_lossless() {
assert_eq!(format!("{CA_PMT}"), "ca_pmt(0x9F8032)");
assert_eq!(
format!("{}", ApduTag::from_bytes(0x9F, 0x99, 0x99)),
"apdu_tag(0x9F9999)"
);
}
}