pub(super) mod application_info;
pub(super) mod conditional_access;
pub(super) mod date_time;
pub(super) mod host_control;
pub(super) mod mmi;
pub(super) mod rm;
use std::{
collections::VecDeque,
fmt,
time::Instant,
};
pub use self::{
application_info::ApplicationInfo,
mmi::MmiMenu,
};
use super::{
apdu::ApduTag,
session::CaEvent,
transport::CiTransport,
};
use crate::error::Result;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct ResourceId(u32);
impl ResourceId {
pub const RESOURCE_MANAGER: Self = Self(0x0001_0041);
pub const APPLICATION_INFORMATION: Self = Self(0x0002_0041);
pub const CONDITIONAL_ACCESS_SUPPORT: Self = Self(0x0003_0041);
pub const HOST_CONTROL: Self = Self(0x0020_0041);
pub const DATE_TIME: Self = Self(0x0024_0041);
pub const MMI: Self = Self(0x0040_0041);
pub const fn new(raw: u32) -> Self {
Self(raw)
}
pub const fn raw(self) -> u32 {
self.0
}
pub const fn base(self) -> u32 {
self.0 >> 6
}
pub const fn version(self) -> u8 {
(self.0 & 0x3F) as u8
}
}
impl fmt::Debug for ResourceId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match *self {
Self::RESOURCE_MANAGER => "RESOURCE_MANAGER",
Self::APPLICATION_INFORMATION => "APPLICATION_INFORMATION",
Self::CONDITIONAL_ACCESS_SUPPORT => "CONDITIONAL_ACCESS_SUPPORT",
Self::HOST_CONTROL => "HOST_CONTROL",
Self::DATE_TIME => "DATE_TIME",
Self::MMI => "MMI",
_ => return write!(f, "ResourceId(0x{:08X})", self.0),
};
write!(f, "ResourceId({})", name)
}
}
pub(super) struct ResourceContext<'a> {
pub transport: &'a mut CiTransport,
pub events: &'a mut VecDeque<CaEvent>,
pub slot_id: u8,
pub session_id: u16,
pub close_session: bool,
}
impl ResourceContext<'_> {
pub fn send_apdu(&mut self, tag: ApduTag, body: &[u8]) -> Result<()> {
self.transport
.send_apdu(self.slot_id, self.session_id, tag, body)
}
pub fn event(&mut self, event: CaEvent) {
self.events.push_back(event);
}
}
pub(super) trait Resource {
fn resource_id(&self) -> ResourceId;
fn on_open(&mut self, _ctx: &mut ResourceContext<'_>) -> Result<()> {
Ok(())
}
fn on_apdu(&mut self, ctx: &mut ResourceContext<'_>, tag: ApduTag, body: &[u8]) -> Result<()>;
fn on_close(&mut self, _slot_id: u8, _session_id: u16) {}
fn on_tick(&mut self, _transport: &mut CiTransport, _now: Instant) -> Result<()> {
Ok(())
}
}
const HOST_PROFILE: [ResourceId; 6] = [
ResourceId::RESOURCE_MANAGER,
ResourceId::APPLICATION_INFORMATION,
ResourceId::CONDITIONAL_ACCESS_SUPPORT,
ResourceId::HOST_CONTROL,
ResourceId::DATE_TIME,
ResourceId::MMI,
];
pub(super) struct ResourceRegistry {
pub rm: rm::ResourceManager,
pub application_info: application_info::ApplicationInfoResource,
pub conditional_access: conditional_access::ConditionalAccessResource,
pub host_control: host_control::HostControlResource,
pub date_time: date_time::DateTimeResource,
pub mmi: mmi::MmiResource,
}
impl ResourceRegistry {
pub fn new() -> Self {
ResourceRegistry {
rm: rm::ResourceManager::new(&HOST_PROFILE),
application_info: application_info::ApplicationInfoResource::new(),
conditional_access: conditional_access::ConditionalAccessResource::new(),
host_control: host_control::HostControlResource,
date_time: date_time::DateTimeResource::new(),
mmi: mmi::MmiResource::new(),
}
}
fn resources(&mut self) -> [&mut dyn Resource; 6] {
[
&mut self.rm,
&mut self.application_info,
&mut self.conditional_access,
&mut self.host_control,
&mut self.date_time,
&mut self.mmi,
]
}
pub fn lookup(&mut self, resource_id: ResourceId) -> Option<&mut dyn Resource> {
self.resources()
.into_iter()
.find(|resource| resource.resource_id().base() == resource_id.base())
}
pub fn tick(&mut self, transport: &mut CiTransport, now: Instant) -> Result<()> {
for resource in self.resources() {
resource.on_tick(transport, now)?;
}
Ok(())
}
}