firewire-dice-protocols 0.3.1

Implementation of protocols defined by TC Applied Technologies for ASICs of Digital Interface Communication Engine (DICE) as well as hardware vendors.
Documentation
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (c) 2020 Takashi Sakamoto

use super::*;

impl DstBlk {
    const ID_MASK: u8 = 0xf0;
    const ID_SHIFT: usize = 4;
    const CH_MASK: u8 = 0x0f;
    const CH_SHIFT: usize = 0;

    const AES_VALUE: u8 = 0;
    const ADAT_VALUE: u8 = 1;
    const MIXER_TX0_VALUE: u8 = 2;
    const MIXER_TX1_VALUE: u8 = 3;
    const INS0_VALUE: u8 = 4;
    const INS1_VALUE: u8 = 5;
    const ARM_APB_AUDIO_VALUE: u8 = 10;
    const AVS0_VALUE: u8 = 11;
    const AVS1_VALUE: u8 = 12;
}

fn serialize_dst_blk(dst: &DstBlk, val: &mut u8) -> Result<(), String> {
    let id = match dst.id {
        DstBlkId::Aes => DstBlk::AES_VALUE,
        DstBlkId::Adat => DstBlk::ADAT_VALUE,
        DstBlkId::MixerTx0 => DstBlk::MIXER_TX0_VALUE,
        DstBlkId::MixerTx1 => DstBlk::MIXER_TX1_VALUE,
        DstBlkId::Ins0 => DstBlk::INS0_VALUE,
        DstBlkId::Ins1 => DstBlk::INS1_VALUE,
        DstBlkId::ArmApbAudio => DstBlk::ARM_APB_AUDIO_VALUE,
        DstBlkId::Avs0 => DstBlk::AVS0_VALUE,
        DstBlkId::Avs1 => DstBlk::AVS1_VALUE,
        DstBlkId::Reserved(id) => id,
    };

    let ch = dst.ch;

    *val =
        ((id << DstBlk::ID_SHIFT) & DstBlk::ID_MASK) | ((ch << DstBlk::CH_SHIFT) & DstBlk::CH_MASK);

    Ok(())
}

fn deserialize_dst_blk(dst: &mut DstBlk, val: &u8) -> Result<(), String> {
    let id = (*val & DstBlk::ID_MASK) >> DstBlk::ID_SHIFT;
    dst.id = match id {
        DstBlk::AES_VALUE => DstBlkId::Aes,
        DstBlk::ADAT_VALUE => DstBlkId::Adat,
        DstBlk::MIXER_TX0_VALUE => DstBlkId::MixerTx0,
        DstBlk::MIXER_TX1_VALUE => DstBlkId::MixerTx1,
        DstBlk::INS0_VALUE => DstBlkId::Ins0,
        DstBlk::INS1_VALUE => DstBlkId::Ins1,
        DstBlk::ARM_APB_AUDIO_VALUE => DstBlkId::ArmApbAudio,
        DstBlk::AVS0_VALUE => DstBlkId::Avs0,
        DstBlk::AVS1_VALUE => DstBlkId::Avs1,
        _ => DstBlkId::Reserved(id),
    };

    dst.ch = (*val & DstBlk::CH_MASK) >> DstBlk::CH_SHIFT;

    Ok(())
}

impl Ord for DstBlk {
    fn cmp(&self, other: &Self) -> Ordering {
        let mut lval = 0u8;
        serialize_dst_blk(self, &mut lval).unwrap();

        let mut rval = 0u8;
        serialize_dst_blk(other, &mut rval).unwrap();

        lval.cmp(&rval)
    }
}

impl PartialOrd for DstBlk {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl SrcBlk {
    const ID_MASK: u8 = 0xf0;
    const ID_SHIFT: usize = 4;
    const CH_MASK: u8 = 0x0f;
    const CH_SHIFT: usize = 0;

    const AES_VALUE: u8 = 0;
    const ADAT_VALUE: u8 = 1;
    const MIXER_VALUE: u8 = 2;
    const INS0_VALUE: u8 = 4;
    const INS1_VALUE: u8 = 5;
    const ARMAPRAUDIO_VALUE: u8 = 10;
    const AVS0_VALUE: u8 = 11;
    const AVS1_VALUE: u8 = 12;
    const MUTE_VALUE: u8 = 15;
}

fn serialize_src_blk(src: &SrcBlk, val: &mut u8) -> Result<(), String> {
    let id = match src.id {
        SrcBlkId::Aes => SrcBlk::AES_VALUE,
        SrcBlkId::Adat => SrcBlk::ADAT_VALUE,
        SrcBlkId::Mixer => SrcBlk::MIXER_VALUE,
        SrcBlkId::Ins0 => SrcBlk::INS0_VALUE,
        SrcBlkId::Ins1 => SrcBlk::INS1_VALUE,
        SrcBlkId::ArmAprAudio => SrcBlk::ARMAPRAUDIO_VALUE,
        SrcBlkId::Avs0 => SrcBlk::AVS0_VALUE,
        SrcBlkId::Avs1 => SrcBlk::AVS1_VALUE,
        SrcBlkId::Mute => SrcBlk::MUTE_VALUE,
        SrcBlkId::Reserved(id) => id,
    };

    let ch = src.ch;

    *val =
        ((id << SrcBlk::ID_SHIFT) & SrcBlk::ID_MASK) | ((ch << SrcBlk::CH_SHIFT) & SrcBlk::CH_MASK);

    Ok(())
}

fn deserialize_src_blk(src: &mut SrcBlk, val: &u8) -> Result<(), String> {
    let id = (*val & SrcBlk::ID_MASK) >> SrcBlk::ID_SHIFT;
    src.id = match id {
        SrcBlk::AES_VALUE => SrcBlkId::Aes,
        SrcBlk::ADAT_VALUE => SrcBlkId::Adat,
        SrcBlk::MIXER_VALUE => SrcBlkId::Mixer,
        SrcBlk::INS0_VALUE => SrcBlkId::Ins0,
        SrcBlk::INS1_VALUE => SrcBlkId::Ins1,
        SrcBlk::ARMAPRAUDIO_VALUE => SrcBlkId::ArmAprAudio,
        SrcBlk::AVS0_VALUE => SrcBlkId::Avs0,
        SrcBlk::AVS1_VALUE => SrcBlkId::Avs1,
        SrcBlk::MUTE_VALUE => SrcBlkId::Mute,
        _ => SrcBlkId::Reserved(id),
    };

    src.ch = (*val & SrcBlk::CH_MASK) >> SrcBlk::CH_SHIFT;

    Ok(())
}

impl Ord for SrcBlk {
    fn cmp(&self, other: &Self) -> Ordering {
        let mut lval = 0u8;
        serialize_src_blk(self, &mut lval).unwrap();

        let mut rval = 0u8;
        serialize_src_blk(other, &mut rval).unwrap();

        lval.cmp(&rval)
    }
}

impl PartialOrd for SrcBlk {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl RouterEntry {
    const SIZE: usize = 4;

    const DST_MASK: u32 = 0x000000ff;
    const DST_SHIFT: usize = 0;

    const SRC_MASK: u32 = 0x0000ff00;
    const SRC_SHIFT: usize = 8;

    const PEAK_MASK: u32 = 0xffff0000;
    const PEAK_SHIFT: usize = 16;
}

pub(crate) fn serialize_router_entry(entry: &RouterEntry, raw: &mut [u8]) -> Result<(), String> {
    assert!(raw.len() >= RouterEntry::SIZE);

    let mut dst_val = 0u8;
    serialize_dst_blk(&entry.dst, &mut dst_val)?;

    let mut src_val = 0u8;
    serialize_src_blk(&entry.src, &mut src_val)?;

    let val = (((entry.peak as u32) << RouterEntry::PEAK_SHIFT) & RouterEntry::PEAK_MASK)
        | (((src_val as u32) << RouterEntry::SRC_SHIFT) & RouterEntry::SRC_MASK)
        | (((dst_val as u32) << RouterEntry::DST_SHIFT) & RouterEntry::DST_MASK);
    serialize_u32(&val, raw);

    Ok(())
}

pub(crate) fn deserialize_router_entry(entry: &mut RouterEntry, raw: &[u8]) -> Result<(), String> {
    assert!(raw.len() >= RouterEntry::SIZE);

    let mut val = 0u32;
    deserialize_u32(&mut val, &raw);

    let dst_val = ((val & RouterEntry::DST_MASK) >> RouterEntry::DST_SHIFT) as u8;
    deserialize_dst_blk(&mut entry.dst, &dst_val)?;

    let src_val = ((val & RouterEntry::SRC_MASK) >> RouterEntry::SRC_SHIFT) as u8;
    deserialize_src_blk(&mut entry.src, &src_val)?;

    entry.peak = ((val & RouterEntry::PEAK_MASK) >> RouterEntry::PEAK_SHIFT) as u16;

    Ok(())
}

pub(crate) fn calculate_router_entries_size(entry_count: usize) -> usize {
    entry_count * RouterEntry::SIZE
}

pub(crate) fn serialize_router_entries(
    entries: &[RouterEntry],
    raw: &mut [u8],
) -> Result<(), String> {
    assert!(raw.len() >= calculate_router_entries_size(entries.len()));

    entries.iter().enumerate().try_for_each(|(i, entry)| {
        let pos = i * RouterEntry::SIZE;
        serialize_router_entry(entry, &mut raw[pos..(pos + RouterEntry::SIZE)])
    })
}

pub(crate) fn deserialize_router_entries(
    entries: &mut [RouterEntry],
    raw: &[u8],
) -> Result<(), String> {
    assert!(raw.len() >= calculate_router_entries_size(entries.len()));

    entries.iter_mut().enumerate().try_for_each(|(i, entry)| {
        let pos = i * RouterEntry::SIZE;
        deserialize_router_entry(entry, &raw[pos..(pos + RouterEntry::SIZE)])
    })
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn dst_blk_serdes() {
        let params = DstBlk {
            id: DstBlkId::Ins1,
            ch: 10,
        };
        let mut val = 0;
        serialize_dst_blk(&params, &mut val).unwrap();

        let mut p = DstBlk::default();
        deserialize_dst_blk(&mut p, &val).unwrap();

        assert_eq!(params, p);
    }

    #[test]
    fn src_blk_serdes() {
        let params = SrcBlk {
            id: SrcBlkId::Ins1,
            ch: 10,
        };
        let mut val = 0;
        serialize_src_blk(&params, &mut val).unwrap();

        let mut p = SrcBlk::default();
        deserialize_src_blk(&mut p, &val).unwrap();

        assert_eq!(params, p);
    }
}