#![doc = include_str!("../README.md")]
pub mod command_dsp;
pub mod config_rom;
pub mod register_dsp;
pub mod version_1;
pub mod version_2;
pub mod version_3;
use {
glib::{Error, FileError},
hinawa::{prelude::{FwNodeExt, FwReqExtManual}, FwNode, FwReq, FwTcode},
std::{thread, time},
};
const BASE_OFFSET: u64 = 0xfffff0000000;
const OFFSET_CLK: u32 = 0x0b14;
const OFFSET_PORT: u32 = 0x0c04;
const OFFSET_CLK_DISPLAY: u32 = 0x0c60;
fn read_quad(req: &FwReq, node: &mut FwNode, offset: u32, timeout_ms: u32) -> Result<u32, Error> {
let mut frame = [0; 4];
req.transaction_sync(
node,
FwTcode::ReadQuadletRequest,
BASE_OFFSET + offset as u64,
4,
&mut frame,
timeout_ms,
)
.map(|_| u32::from_be_bytes(frame))
}
fn write_quad(
req: &FwReq,
node: &mut FwNode,
offset: u32,
quad: u32,
timeout_ms: u32,
) -> Result<(), Error> {
let mut frame = [0; 4];
frame.copy_from_slice(&quad.to_be_bytes());
req.transaction_sync(
node,
FwTcode::WriteQuadletRequest,
BASE_OFFSET + offset as u64,
4,
&mut frame,
timeout_ms,
)
.or_else(|err| {
thread::sleep(time::Duration::from_millis(BUSY_DURATION));
req.transaction_sync(
node,
FwTcode::WriteQuadletRequest,
BASE_OFFSET + offset as u64,
4,
&mut frame,
timeout_ms,
)
.and_then(|_| {
if u32::from_be_bytes(frame) == quad {
Ok(())
} else {
Err(err)
}
})
})
}
fn get_idx_from_val(
offset: u32,
mask: u32,
shift: usize,
label: &str,
req: &FwReq,
node: &mut FwNode,
vals: &[u8],
timeout_ms: u32,
) -> Result<usize, Error> {
let quad = read_quad(req, node, offset, timeout_ms)?;
let val = ((quad & mask) >> shift) as u8;
vals.iter().position(|&v| v == val).ok_or_else(|| {
let label = format!("Detect invalid value for {}: {:02x}", label, val);
Error::new(FileError::Io, &label)
})
}
fn set_idx_to_val(
offset: u32,
mask: u32,
shift: usize,
label: &str,
req: &FwReq,
node: &mut FwNode,
vals: &[u8],
idx: usize,
timeout_ms: u32,
) -> Result<(), Error> {
if idx >= vals.len() {
let label = format!("Invalid argument for {}: {} {}", label, vals.len(), idx);
return Err(Error::new(FileError::Inval, &label));
}
let mut quad = read_quad(req, node, offset, timeout_ms)?;
quad &= !mask;
quad |= (vals[idx] as u32) << shift;
write_quad(req, node, offset, quad, timeout_ms)
}
pub enum ClkRate {
R44100,
R48000,
R88200,
R96000,
R176400,
R192000,
}
const BUSY_DURATION: u64 = 150;
const DISPLAY_CHARS: usize = 4 * 4;
fn update_clk_display(
req: &FwReq,
node: &mut FwNode,
label: &str,
timeout_ms: u32,
) -> Result<(), Error> {
let mut chars = [0x20; DISPLAY_CHARS];
chars
.iter_mut()
.zip(label.bytes())
.for_each(|(c, l)| *c = l);
(0..(DISPLAY_CHARS / 4)).try_for_each(|i| {
let mut frame = [0; 4];
frame.copy_from_slice(&chars[(i * 4)..(i * 4 + 4)]);
frame.reverse();
let quad = u32::from_ne_bytes(frame);
let offset = OFFSET_CLK_DISPLAY + 4 * i as u32;
write_quad(req, node, offset, quad, timeout_ms)
})
}
const PORT_PHONE_LABEL: &str = "phone-assign";
const PORT_PHONE_MASK: u32 = 0x0000000f;
const PORT_PHONE_SHIFT: usize = 0;
pub trait AssignOperation {
const ASSIGN_PORTS: &'static [(TargetPort, u8)];
fn get_phone_assign(
req: &mut FwReq,
node: &mut FwNode,
timeout_ms: u32,
) -> Result<usize, Error> {
let vals: Vec<u8> = Self::ASSIGN_PORTS.iter().map(|e| e.1).collect();
get_idx_from_val(
OFFSET_PORT,
PORT_PHONE_MASK,
PORT_PHONE_SHIFT,
PORT_PHONE_LABEL,
req,
node,
&vals,
timeout_ms,
)
}
fn set_phone_assign(
req: &mut FwReq,
node: &mut FwNode,
idx: usize,
timeout_ms: u32,
) -> Result<(), Error> {
let vals: Vec<u8> = Self::ASSIGN_PORTS.iter().map(|e| e.1).collect();
set_idx_to_val(
OFFSET_PORT,
PORT_PHONE_MASK,
PORT_PHONE_SHIFT,
PORT_PHONE_LABEL,
req,
node,
&vals,
idx,
timeout_ms,
)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum WordClkSpeedMode {
ForceLowRate,
FollowSystemClk,
}
impl Default for WordClkSpeedMode {
fn default() -> Self {
Self::FollowSystemClk
}
}
const WORD_OUT_LABEL: &str = "word-out";
const WORD_OUT_MASK: u32 = 0x08000000;
const WORD_OUT_SHIFT: usize = 27;
const WORD_OUT_VALS: [u8; 2] = [0x00, 0x01];
pub trait WordClkOperation {
fn get_word_out(
req: &mut FwReq,
node: &mut FwNode,
timeout_ms: u32,
) -> Result<WordClkSpeedMode, Error> {
get_idx_from_val(
OFFSET_CLK,
WORD_OUT_MASK,
WORD_OUT_SHIFT,
WORD_OUT_LABEL,
req,
node,
&WORD_OUT_VALS,
timeout_ms,
)
.map(|val| {
if val == 0 {
WordClkSpeedMode::ForceLowRate
} else {
WordClkSpeedMode::FollowSystemClk
}
})
}
fn set_word_out(
req: &mut FwReq,
node: &mut FwNode,
mode: WordClkSpeedMode,
timeout_ms: u32,
) -> Result<(), Error> {
let idx = match mode {
WordClkSpeedMode::ForceLowRate => 0,
WordClkSpeedMode::FollowSystemClk => 1,
};
set_idx_to_val(
OFFSET_CLK,
WORD_OUT_MASK,
WORD_OUT_SHIFT,
WORD_OUT_LABEL,
req,
node,
&WORD_OUT_VALS,
idx,
timeout_ms,
)
}
}
pub enum AesebuRateConvertMode {
None,
InputToSystem,
OutputDependsInput,
OutputDoubleSystem,
}
const AESEBU_RATE_CONVERT_LABEL: &str = "aesebu-rate-convert";
pub trait AesebuRateConvertOperation {
const AESEBU_RATE_CONVERT_MASK: u32;
const AESEBU_RATE_CONVERT_SHIFT: usize;
const AESEBU_RATE_CONVERT_VALS: [u8; 4] = [0x00, 0x01, 0x02, 0x03];
const AESEBU_RATE_CONVERT_MODES: [AesebuRateConvertMode; 4] = [
AesebuRateConvertMode::None,
AesebuRateConvertMode::InputToSystem,
AesebuRateConvertMode::OutputDependsInput,
AesebuRateConvertMode::OutputDoubleSystem,
];
fn get_aesebu_rate_convert_mode(
req: &mut FwReq,
node: &mut FwNode,
timeout_ms: u32,
) -> Result<usize, Error> {
get_idx_from_val(
OFFSET_CLK,
Self::AESEBU_RATE_CONVERT_MASK,
Self::AESEBU_RATE_CONVERT_SHIFT,
AESEBU_RATE_CONVERT_LABEL,
req,
node,
&Self::AESEBU_RATE_CONVERT_VALS,
timeout_ms,
)
}
fn set_aesebu_rate_convert_mode(
req: &mut FwReq,
node: &mut FwNode,
idx: usize,
timeout_ms: u32,
) -> Result<(), Error> {
set_idx_to_val(
OFFSET_CLK,
Self::AESEBU_RATE_CONVERT_MASK,
Self::AESEBU_RATE_CONVERT_SHIFT,
AESEBU_RATE_CONVERT_LABEL,
req,
node,
&Self::AESEBU_RATE_CONVERT_VALS,
idx,
timeout_ms,
)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum LevelMetersHoldTimeMode {
Off,
Sec2,
Sec4,
Sec10,
Sec60,
Sec300,
Sec480,
Infinite,
}
impl Default for LevelMetersHoldTimeMode {
fn default() -> Self {
Self::Off
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum LevelMetersProgrammableMode {
AnalogOutput,
AdatInput,
AdatOutput,
}
impl Default for LevelMetersProgrammableMode {
fn default() -> Self {
Self::AnalogOutput
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum LevelMetersAesebuMode {
Input,
Output,
}
impl Default for LevelMetersAesebuMode {
fn default() -> Self {
Self::Input
}
}
const LEVEL_METERS_OFFSET: u32 = 0x0b24;
const LEVEL_METERS_PEAK_HOLD_TIME_MASK: u32 = 0x00003800;
const LEVEL_METERS_PEAK_HOLD_TIME_SHIFT: usize = 11;
const LEVEL_METERS_CLIP_HOLD_TIME_MASK: u32 = 0x00000700;
const LEVEL_METERS_CLIP_HOLD_TIME_SHIFT: usize = 8;
const LEVEL_METERS_HOLD_TIME_VALS: [u8; 8] = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
const LEVEL_METERS_AESEBU_MASK: u32 = 0x00000004;
const LEVEL_METERS_AESEBU_SHIFT: usize = 2;
const LEVEL_METERS_AESEBU_VALS: [u8; 2] = [0x00, 0x01];
const LEVEL_METERS_PROGRAMMABLE_MASK: u32 = 0x00000003;
const LEVEL_METERS_PROGRAMMABLE_SHIFT: usize = 0;
const LEVEL_METERS_PROGRAMMABLE_VALS: [u8; 3] = [0x00, 0x01, 0x02];
const LEVEL_METERS_PEAK_HOLD_TIME_LABEL: &str = "level-meters-peak-hold-time";
const LEVEL_METERS_CLIP_HOLD_TIME_LABEL: &str = "level-meters-clip-hold-time";
const LEVEL_METERS_PROGRAMMABLE_LABEL: &str = "level-meters-programmable";
const LEVEL_METERS_AESEBU_LABEL: &str = "level-meters-aesebu";
pub trait LevelMetersOperation {
const LEVEL_METERS_HOLD_TIME_MODES: [LevelMetersHoldTimeMode; 8] = [
LevelMetersHoldTimeMode::Off,
LevelMetersHoldTimeMode::Sec2,
LevelMetersHoldTimeMode::Sec4,
LevelMetersHoldTimeMode::Sec10,
LevelMetersHoldTimeMode::Sec60,
LevelMetersHoldTimeMode::Sec300,
LevelMetersHoldTimeMode::Sec480,
LevelMetersHoldTimeMode::Infinite,
];
const LEVEL_METERS_AESEBU_MODES: [LevelMetersAesebuMode; 2] =
[LevelMetersAesebuMode::Output, LevelMetersAesebuMode::Input];
const LEVEL_METERS_PROGRAMMABLE_MODES: [LevelMetersProgrammableMode; 3] = [
LevelMetersProgrammableMode::AnalogOutput,
LevelMetersProgrammableMode::AdatInput,
LevelMetersProgrammableMode::AdatOutput,
];
fn get_level_meters_peak_hold_time_mode(
req: &mut FwReq,
node: &mut FwNode,
timeout_ms: u32,
) -> Result<usize, Error> {
get_idx_from_val(
LEVEL_METERS_OFFSET,
LEVEL_METERS_PEAK_HOLD_TIME_MASK,
LEVEL_METERS_PEAK_HOLD_TIME_SHIFT,
LEVEL_METERS_PEAK_HOLD_TIME_LABEL,
req,
node,
&LEVEL_METERS_HOLD_TIME_VALS,
timeout_ms,
)
}
fn set_level_meters_peak_hold_time_mode(
req: &mut FwReq,
node: &mut FwNode,
idx: usize,
timeout_ms: u32,
) -> Result<(), Error> {
set_idx_to_val(
LEVEL_METERS_OFFSET,
LEVEL_METERS_PEAK_HOLD_TIME_MASK,
LEVEL_METERS_PEAK_HOLD_TIME_SHIFT,
LEVEL_METERS_PEAK_HOLD_TIME_LABEL,
req,
node,
&LEVEL_METERS_HOLD_TIME_VALS,
idx,
timeout_ms,
)
}
fn get_level_meters_clip_hold_time_mode(
req: &mut FwReq,
node: &mut FwNode,
timeout_ms: u32,
) -> Result<usize, Error> {
get_idx_from_val(
LEVEL_METERS_OFFSET,
LEVEL_METERS_CLIP_HOLD_TIME_MASK,
LEVEL_METERS_CLIP_HOLD_TIME_SHIFT,
LEVEL_METERS_CLIP_HOLD_TIME_LABEL,
req,
node,
&LEVEL_METERS_HOLD_TIME_VALS,
timeout_ms,
)
}
fn set_level_meters_clip_hold_time_mode(
req: &mut FwReq,
node: &mut FwNode,
idx: usize,
timeout_ms: u32,
) -> Result<(), Error> {
set_idx_to_val(
LEVEL_METERS_OFFSET,
LEVEL_METERS_CLIP_HOLD_TIME_MASK,
LEVEL_METERS_CLIP_HOLD_TIME_SHIFT,
LEVEL_METERS_CLIP_HOLD_TIME_LABEL,
req,
node,
&LEVEL_METERS_HOLD_TIME_VALS,
idx,
timeout_ms,
)
}
fn get_level_meters_aesebu_mode(
req: &mut FwReq,
node: &mut FwNode,
timeout_ms: u32,
) -> Result<usize, Error> {
get_idx_from_val(
LEVEL_METERS_OFFSET,
LEVEL_METERS_AESEBU_MASK,
LEVEL_METERS_AESEBU_SHIFT,
LEVEL_METERS_AESEBU_LABEL,
req,
node,
&LEVEL_METERS_AESEBU_VALS,
timeout_ms,
)
}
fn set_level_meters_aesebu_mode(
req: &mut FwReq,
node: &mut FwNode,
idx: usize,
timeout_ms: u32,
) -> Result<(), Error> {
set_idx_to_val(
LEVEL_METERS_OFFSET,
LEVEL_METERS_AESEBU_MASK,
LEVEL_METERS_AESEBU_SHIFT,
LEVEL_METERS_AESEBU_LABEL,
req,
node,
&LEVEL_METERS_AESEBU_VALS,
idx,
timeout_ms,
)
}
fn get_level_meters_programmable_mode(
req: &mut FwReq,
node: &mut FwNode,
timeout_ms: u32,
) -> Result<usize, Error> {
get_idx_from_val(
LEVEL_METERS_OFFSET,
LEVEL_METERS_PROGRAMMABLE_MASK,
LEVEL_METERS_PROGRAMMABLE_SHIFT,
LEVEL_METERS_PROGRAMMABLE_LABEL,
req,
node,
&LEVEL_METERS_PROGRAMMABLE_VALS,
timeout_ms,
)
}
fn set_level_meters_programmable_mode(
req: &mut FwReq,
node: &mut FwNode,
idx: usize,
timeout_ms: u32,
) -> Result<(), Error> {
set_idx_to_val(
LEVEL_METERS_OFFSET,
LEVEL_METERS_PROGRAMMABLE_MASK,
LEVEL_METERS_PROGRAMMABLE_SHIFT,
LEVEL_METERS_PROGRAMMABLE_LABEL,
req,
node,
&LEVEL_METERS_PROGRAMMABLE_VALS,
idx,
timeout_ms,
)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum TargetPort {
Disabled,
AnalogPair(usize),
AesEbuPair,
PhonePair,
MainPair,
SpdifPair,
AdatPair(usize),
Analog6Pairs,
Analog8Pairs,
OpticalAPair(usize),
OpticalBPair(usize),
Analog(usize),
AesEbu(usize),
Phone(usize),
Main(usize),
Spdif(usize),
Adat(usize),
OpticalA(usize),
OpticalB(usize),
}
impl Default for TargetPort {
fn default() -> Self {
Self::Disabled
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum NominalSignalLevel {
Consumer,
Professional,
}
impl Default for NominalSignalLevel {
fn default() -> Self {
Self::Consumer
}
}