sdmmc_core/command/class/class2/cmd20/
arg.rsuse crate::lib_bitfield;
use crate::result::{Error, Result};
mod scc;
mod vsc;
mod vscc;
pub use scc::*;
pub use vsc::*;
pub use vscc::*;
lib_bitfield! {
pub Argument(u32): u32 {
raw_scc: 31, 28;
raw_vsc: 27;
raw_cnt_id: 26, 24;
pub address: 21, 0;
}
}
impl Argument {
pub const LEN: usize = 4;
pub const DEFAULT: u32 = 0;
pub const fn new() -> Self {
Self(Self::DEFAULT)
}
pub const fn vsc(&self) -> VideoSpeedClass {
VideoSpeedClass::from_bool(self.raw_vsc())
}
pub fn set_vsc(&mut self, val: VideoSpeedClass) {
self.set_raw_vsc(val.into_bool());
}
pub const fn scc(&self) -> Result<SpeedClassControl> {
match self.vsc() {
VideoSpeedClass::LegacyUHS => SpeedClassControl::from_raw(self.raw_scc() as u8),
VideoSpeedClass::Video => Err(Error::invalid_field_variant(
"cmd::argument::vsc",
self.raw_vsc() as usize,
)),
}
}
pub fn set_scc(&mut self, val: SpeedClassControl) {
self.set_vsc(VideoSpeedClass::LegacyUHS);
self.set_raw_scc(val.into_raw() as u32);
}
pub const fn vscc(&self) -> Result<VideoSpeedClassControl> {
match self.vsc() {
VideoSpeedClass::LegacyUHS => Err(Error::invalid_field_variant(
"cmd::argument::vsc",
self.raw_vsc() as usize,
)),
VideoSpeedClass::Video => VideoSpeedClassControl::from_raw(self.raw_scc() as u8),
}
}
pub fn set_vscc(&mut self, val: VideoSpeedClassControl) {
self.set_vsc(VideoSpeedClass::Video);
self.set_raw_scc(val.into_raw() as u32);
}
pub const fn cnt_id(&self) -> u8 {
self.raw_cnt_id() as u8
}
pub fn set_cnt_id(&mut self, val: u8) {
self.set_raw_cnt_id(val as u32);
}
pub const fn try_from_bits(val: u32) -> Result<Self> {
match Self(val) {
a if !a.raw_vsc() && a.scc().is_err() => Err(Error::invalid_field_variant(
"cmd::argument::scc",
a.raw_scc() as usize,
)),
a if a.raw_vsc() && a.vscc().is_err() => Err(Error::invalid_field_variant(
"cmd::argument::vscc",
a.raw_scc() as usize,
)),
a => Ok(a),
}
}
pub const fn bytes(&self) -> [u8; Self::LEN] {
self.0.to_be_bytes()
}
pub const fn try_from_bytes(val: &[u8]) -> Result<Self> {
match val.len() {
len if len < Self::LEN => Err(Error::invalid_length(len, Self::LEN)),
_ => Self::try_from_bits(u32::from_be_bytes([val[0], val[1], val[2], val[3]])),
}
}
}
impl Default for Argument {
fn default() -> Self {
Self::new()
}
}
impl From<Argument> for u32 {
fn from(val: Argument) -> Self {
val.bits()
}
}
impl From<Argument> for [u8; Argument::LEN] {
fn from(val: Argument) -> Self {
val.bytes()
}
}
impl TryFrom<u32> for Argument {
type Error = Error;
fn try_from(val: u32) -> Result<Self> {
Self::try_from_bits(val)
}
}
impl TryFrom<&[u8]> for Argument {
type Error = Error;
fn try_from(val: &[u8]) -> Result<Self> {
Self::try_from_bytes(val)
}
}
impl<const N: usize> TryFrom<&[u8; N]> for Argument {
type Error = Error;
fn try_from(val: &[u8; N]) -> Result<Self> {
Self::try_from_bytes(val.as_ref())
}
}
impl<const N: usize> TryFrom<[u8; N]> for Argument {
type Error = Error;
fn try_from(val: [u8; N]) -> Result<Self> {
Self::try_from_bytes(val.as_ref())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fields() {
let mut arg = Argument::new();
assert_eq!(arg.address(), 0);
assert_eq!(arg.cnt_id(), 0);
assert_eq!(arg.vsc(), VideoSpeedClass::LegacyUHS);
arg.set_vsc(VideoSpeedClass::Video);
assert_eq!(arg.vsc(), VideoSpeedClass::Video);
arg.set_vsc(VideoSpeedClass::LegacyUHS);
assert_eq!(arg.scc(), Ok(SpeedClassControl::StartRecording));
arg.set_scc(SpeedClassControl::UpdateDIR);
assert_eq!(arg.scc(), Ok(SpeedClassControl::UpdateDIR));
assert_eq!(
arg.bits(),
(SpeedClassControl::UpdateDIR.into_raw() as u32) << 28
);
arg.set_scc(SpeedClassControl::StartRecording);
assert_eq!(arg.scc(), Ok(SpeedClassControl::StartRecording));
[
VideoSpeedClassControl::UpdateDIR,
VideoSpeedClassControl::UpdateCI,
VideoSpeedClassControl::SuspendRecording,
VideoSpeedClassControl::ResumeRecording,
VideoSpeedClassControl::SetFreeAU,
VideoSpeedClassControl::ReleaseDIR,
]
.into_iter()
.for_each(|exp_vscc| {
arg.set_vscc(exp_vscc);
assert_eq!(arg.vscc(), Ok(exp_vscc));
assert_eq!(arg.bits() & (0xf << 28), (exp_vscc.into_raw() as u32) << 28);
});
arg.set_vscc(VideoSpeedClassControl::StartRecording);
(1..=21u32)
.map(|r| ((1u32 << r) - 1))
.for_each(|exp_address| {
let address = 0x3a_5555;
arg.set_address(address);
assert_eq!(arg.address(), address);
assert_eq!(arg.bits() & 0x3f_ffff, address);
});
(1..=u32::BITS)
.map(|r| ((1u64 << r) - 1) as u32)
.for_each(|raw_arg| {
let raw = raw_arg.to_be_bytes();
let raw_scc = ((raw_arg & (0xfu32 << 28)) >> 28) as u8;
let raw_vsc = ((raw_arg & (1u32 << 27)) >> 27) != 0;
let exp_arg = Argument(raw_arg);
match (
VideoSpeedClass::from_bool(raw_vsc),
SpeedClassControl::from_raw(raw_scc),
VideoSpeedClassControl::from_raw(raw_scc),
) {
(vsc @ VideoSpeedClass::LegacyUHS, Ok(_), _)
| (vsc @ VideoSpeedClass::Video, _, Ok(_)) => {
assert_eq!(Argument::try_from_bits(raw_arg), Ok(exp_arg));
assert_eq!(Argument::try_from_bytes(raw.as_ref()), Ok(exp_arg));
assert_eq!(Argument::try_from(raw_arg), Ok(exp_arg));
assert_eq!(Argument::try_from(raw), Ok(exp_arg));
assert_eq!(Argument::try_from(&raw), Ok(exp_arg));
assert_eq!(exp_arg.bits(), raw_arg);
assert_eq!(exp_arg.bytes(), raw);
match vsc {
VideoSpeedClass::LegacyUHS => {
let exp_scc = SpeedClassControl::from_raw(raw_scc).unwrap();
assert_eq!(exp_arg.vsc(), vsc);
assert_eq!(exp_arg.scc(), Ok(exp_scc));
assert_eq!(
exp_arg.vscc(),
Err(Error::invalid_field_variant(
"cmd::argument::vsc",
vsc as usize
))
);
}
VideoSpeedClass::Video => {
let exp_vscc = VideoSpeedClassControl::from_raw(raw_scc).unwrap();
assert_eq!(exp_arg.vsc(), vsc);
assert_eq!(
exp_arg.scc(),
Err(Error::invalid_field_variant(
"cmd::argument::vsc",
vsc as usize
))
);
assert_eq!(exp_arg.vscc(), Ok(exp_vscc));
}
}
}
(vsc @ VideoSpeedClass::LegacyUHS, Err(_), _)
| (vsc @ VideoSpeedClass::Video, _, Err(_)) => {
let exp_err = match vsc {
VideoSpeedClass::LegacyUHS => {
Error::invalid_field_variant("cmd::argument::scc", raw_scc as usize)
}
VideoSpeedClass::Video => Error::invalid_field_variant(
"cmd::argument::vscc",
raw_scc as usize,
),
};
assert_eq!(Argument::try_from_bits(raw_arg), Err(exp_err));
assert_eq!(Argument::try_from_bytes(raw.as_ref()), Err(exp_err));
assert_eq!(Argument::try_from(raw_arg), Err(exp_err));
assert_eq!(Argument::try_from(raw), Err(exp_err));
assert_eq!(Argument::try_from(&raw), Err(exp_err));
}
}
});
}
}