#![doc = include_str!("../README.md")]
pub mod bridgeco;
pub mod apogee;
pub mod behringer;
pub mod digidesign;
pub mod esi;
pub mod focusrite;
pub mod icon;
pub mod maudio;
pub mod presonus;
pub mod roland;
pub mod stanton;
pub mod terratec;
pub mod yamaha_terratec;
use {
self::bridgeco::{ExtendedStreamFormatSingle, *},
glib::{Error, FileError, IsA},
hinawa::{
prelude::{FwFcpExt, FwFcpExtManual, FwReqExtManual},
FwFcp, FwNode, FwReq, FwTcode,
},
ta1394_avc_audio::{amdtp::*, *},
ta1394_avc_ccm::*,
ta1394_avc_general::{general::*, *},
ta1394_avc_stream_format::*,
};
const DM_APPL_OFFSET: u64 = 0xffc700000000;
const DM_APPL_METER_OFFSET: u64 = DM_APPL_OFFSET + 0x00600000;
const DM_APPL_PARAM_OFFSET: u64 = DM_APPL_OFFSET + 0x00700000;
const DM_BCO_OFFSET: u64 = 0xffffc8000000;
const DM_BCO_BOOTLOADER_INFO_OFFSET: u64 = DM_BCO_OFFSET + 0x00020000;
#[derive(Default, Debug)]
pub struct BebobAvc(FwFcp);
impl Ta1394Avc<Error> for BebobAvc {
fn transaction(&self, command_frame: &[u8], timeout_ms: u32) -> Result<Vec<u8>, Error> {
let mut resp = vec![0; Self::FRAME_SIZE];
self.0
.avc_transaction(&command_frame, &mut resp, timeout_ms)
.map(|len| {
resp.truncate(len);
resp
})
}
fn control<O: AvcOp + AvcControl>(
&self,
addr: &AvcAddr,
op: &mut O,
timeout_ms: u32,
) -> Result<(), Ta1394AvcError<Error>> {
let operands =
AvcControl::build_operands(op, addr).map_err(|err| Ta1394AvcError::CmdBuild(err))?;
let command_frame =
Self::compose_command_frame(AvcCmdType::Control, addr, O::OPCODE, &operands)?;
let response_frame = self
.transaction(&command_frame, timeout_ms)
.map_err(|cause| Ta1394AvcError::CommunicationFailure(cause))?;
Self::detect_response_operands(&response_frame, addr, O::OPCODE)
.and_then(|(rcode, operands)| {
let expected = match O::OPCODE {
InputPlugSignalFormat::OPCODE
| OutputPlugSignalFormat::OPCODE
| SignalSource::OPCODE => {
rcode == AvcRespCode::Accepted || rcode == AvcRespCode::Reserved(0x00)
}
_ => rcode == AvcRespCode::Accepted,
};
if !expected {
Err(AvcRespParseError::UnexpectedStatus)
} else {
AvcControl::parse_operands(op, addr, &operands)
}
})
.map_err(|err| Ta1394AvcError::RespParse(err))
}
}
impl BebobAvc {
pub fn bind(&self, node: &impl IsA<FwNode>) -> Result<(), Error> {
self.0.bind(node)
}
pub fn control<O: AvcOp + AvcControl>(
&self,
addr: &AvcAddr,
op: &mut O,
timeout_ms: u32,
) -> Result<(), Error> {
Ta1394Avc::<Error>::control(self, addr, op, timeout_ms).map_err(|err| from_avc_err(err))
}
pub fn status<O: AvcOp + AvcStatus>(
&self,
addr: &AvcAddr,
op: &mut O,
timeout_ms: u32,
) -> Result<(), Error> {
Ta1394Avc::<Error>::status(self, addr, op, timeout_ms).map_err(|err| from_avc_err(err))
}
}
fn from_avc_err(err: Ta1394AvcError<Error>) -> Error {
match err {
Ta1394AvcError::CmdBuild(cause) => Error::new(FileError::Inval, &cause.to_string()),
Ta1394AvcError::CommunicationFailure(cause) => cause,
Ta1394AvcError::RespParse(cause) => Error::new(FileError::Io, &cause.to_string()),
}
}
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
pub struct MediaClockParameters {
pub freq_idx: usize,
}
pub trait MediaClockFrequencyOperation {
const FREQ_LIST: &'static [u32];
fn cache_freq(
avc: &BebobAvc,
params: &mut MediaClockParameters,
timeout_ms: u32,
) -> Result<(), Error> {
let plug_addr =
BcoPlugAddr::new_for_unit(BcoPlugDirection::Output, BcoPlugAddrUnitType::Isoc, 0);
let mut op = ExtendedStreamFormatSingle::new(&plug_addr);
avc.status(&AvcAddr::Unit, &mut op, timeout_ms)?;
op.stream_format
.as_bco_compound_am824_stream()
.ok_or_else(|| {
let label = "Bco Compound AM824 stream is not available for the unit";
Error::new(FileError::Nxio, &label)
})
.and_then(|format| {
Self::FREQ_LIST
.iter()
.position(|&r| r == format.freq)
.ok_or_else(|| {
let msg = format!("Unexpected entry for source of clock: {}", format.freq);
Error::new(FileError::Io, &msg)
})
})
.map(|freq_idx| params.freq_idx = freq_idx)
}
fn update_freq(
avc: &BebobAvc,
params: &MediaClockParameters,
old: &mut MediaClockParameters,
timeout_ms: u32,
) -> Result<(), Error> {
let fdf = Self::FREQ_LIST
.iter()
.nth(params.freq_idx)
.ok_or_else(|| {
let msg = format!(
"Invalid argument for index of frequency: {}",
params.freq_idx
);
Error::new(FileError::Inval, &msg)
})
.map(|&freq| AmdtpFdf::new(AmdtpEventType::Am824, false, freq))?;
let mut op = InputPlugSignalFormat(PlugSignalFormat {
plug_id: 0,
fmt: FMT_IS_AMDTP,
fdf: fdf.into(),
});
avc.control(&AvcAddr::Unit, &mut op, timeout_ms)?;
let mut op = OutputPlugSignalFormat(PlugSignalFormat {
plug_id: 0,
fmt: FMT_IS_AMDTP,
fdf: fdf.into(),
});
avc.control(&AvcAddr::Unit, &mut op, timeout_ms)?;
*old = *params;
Ok(())
}
}
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
pub struct SamplingClockParameters {
pub src_idx: usize,
}
pub trait SamplingClockSourceOperation {
const DST: SignalAddr;
const SRC_LIST: &'static [SignalAddr];
fn cache_src(
avc: &BebobAvc,
params: &mut SamplingClockParameters,
timeout_ms: u32,
) -> Result<(), Error> {
let mut op = SignalSource::new(&Self::DST);
avc.status(&AvcAddr::Unit, &mut op, timeout_ms)?;
Self::SRC_LIST
.iter()
.position(|&s| s == op.src)
.ok_or_else(|| {
let label = "Unexpected entry for source of clock";
Error::new(FileError::Io, &label)
})
.map(|src_idx| params.src_idx = src_idx)
}
fn update_src(
avc: &BebobAvc,
params: &SamplingClockParameters,
old: &mut SamplingClockParameters,
timeout_ms: u32,
) -> Result<(), Error> {
let src = Self::SRC_LIST
.iter()
.nth(params.src_idx)
.ok_or_else(|| {
let label = "Invalid value for source of clock";
Error::new(FileError::Inval, &label)
})
.copied()?;
let mut op = SignalSource::new(&Self::DST);
op.src = src;
avc.control(&AvcAddr::Unit, &mut op, timeout_ms)
.map(|_| *old = *params)
}
}
pub trait AvcAudioFeatureSpecification {
const ENTRIES: &'static [(u8, AudioCh)];
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AvcLevelParameters {
pub levels: Vec<i16>,
}
pub trait AvcLevelOperation: AvcAudioFeatureSpecification {
const LEVEL_MIN: i16 = VolumeData::VALUE_NEG_INFINITY;
const LEVEL_MAX: i16 = VolumeData::VALUE_ZERO;
const LEVEL_STEP: i16 = 0x100;
fn create_level_parameters() -> AvcLevelParameters {
AvcLevelParameters {
levels: vec![Default::default(); Self::ENTRIES.len()],
}
}
fn cache_levels(
avc: &BebobAvc,
params: &mut AvcLevelParameters,
timeout_ms: u32,
) -> Result<(), Error> {
assert_eq!(params.levels.len(), Self::ENTRIES.len());
params
.levels
.iter_mut()
.zip(Self::ENTRIES)
.try_for_each(|(level, entry)| {
let &(func_block_id, audio_ch) = entry;
let mut op = AudioFeature::new(
func_block_id,
CtlAttr::Current,
audio_ch,
FeatureCtl::Volume(VolumeData::new(1)),
);
avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
.map(|_| {
if let FeatureCtl::Volume(data) = op.ctl {
*level = data.0[0]
}
})
})
}
fn update_levels(
avc: &BebobAvc,
params: &AvcLevelParameters,
old: &mut AvcLevelParameters,
timeout_ms: u32,
) -> Result<(), Error> {
assert_eq!(params.levels.len(), Self::ENTRIES.len());
assert_eq!(old.levels.len(), Self::ENTRIES.len());
old.levels
.iter_mut()
.zip(params.levels.iter())
.zip(Self::ENTRIES)
.filter(|((old, new), _)| !new.eq(old))
.try_for_each(|((old, new), entry)| {
let &(func_block_id, audio_ch) = entry;
let mut op = AudioFeature::new(
func_block_id,
CtlAttr::Current,
audio_ch,
FeatureCtl::Volume(VolumeData(vec![*new])),
);
avc.control(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
.map(|_| *old = *new)
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AvcLrBalanceParameters {
pub balances: Vec<i16>,
}
pub trait AvcLrBalanceOperation: AvcAudioFeatureSpecification {
const BALANCE_MIN: i16 = LrBalanceData::VALUE_LEFT_NEG_INFINITY;
const BALANCE_MAX: i16 = LrBalanceData::VALUE_LEFT_MAX;
const BALANCE_STEP: i16 = 0x80;
fn create_lr_balance_parameters() -> AvcLrBalanceParameters {
AvcLrBalanceParameters {
balances: vec![Default::default(); Self::ENTRIES.len()],
}
}
fn cache_lr_balances(
avc: &BebobAvc,
params: &mut AvcLrBalanceParameters,
timeout_ms: u32,
) -> Result<(), Error> {
assert_eq!(params.balances.len(), Self::ENTRIES.len());
params
.balances
.iter_mut()
.zip(Self::ENTRIES)
.try_for_each(|(balance, entry)| {
let &(func_block_id, audio_ch) = entry;
let mut op = AudioFeature::new(
func_block_id,
CtlAttr::Current,
audio_ch,
FeatureCtl::LrBalance(Default::default()),
);
avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
.map(|_| {
if let FeatureCtl::LrBalance(data) = op.ctl {
*balance = data.0;
}
})
})
}
fn update_lr_balances(
avc: &BebobAvc,
params: &AvcLrBalanceParameters,
old: &mut AvcLrBalanceParameters,
timeout_ms: u32,
) -> Result<(), Error> {
assert_eq!(params.balances.len(), Self::ENTRIES.len());
assert_eq!(old.balances.len(), Self::ENTRIES.len());
old.balances
.iter_mut()
.zip(params.balances.iter())
.zip(Self::ENTRIES)
.filter(|((o, n), _)| !o.eq(n))
.try_for_each(|((old, &new), entry)| {
let &(func_block_id, audio_ch) = entry;
let mut op = AudioFeature::new(
func_block_id,
CtlAttr::Current,
audio_ch,
FeatureCtl::LrBalance(LrBalanceData(new)),
);
avc.control(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
.map(|_| *old = new)
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AvcMuteParameters {
pub mutes: Vec<bool>,
}
pub trait AvcMuteOperation: AvcAudioFeatureSpecification {
fn create_mute_parameters() -> AvcMuteParameters {
AvcMuteParameters {
mutes: vec![Default::default(); Self::ENTRIES.len()],
}
}
fn cache_mutes(
avc: &BebobAvc,
params: &mut AvcMuteParameters,
timeout_ms: u32,
) -> Result<(), Error> {
assert_eq!(params.mutes.len(), Self::ENTRIES.len());
params
.mutes
.iter_mut()
.zip(Self::ENTRIES)
.try_for_each(|(mute, entry)| {
let &(func_block_id, audio_ch) = entry;
let mut op = AudioFeature::new(
func_block_id,
CtlAttr::Current,
audio_ch,
FeatureCtl::Mute(vec![false]),
);
avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
.map(|_| {
if let FeatureCtl::Mute(data) = op.ctl {
*mute = data[0];
}
})
})
}
fn update_mutes(
avc: &BebobAvc,
params: &AvcMuteParameters,
old: &mut AvcMuteParameters,
timeout_ms: u32,
) -> Result<(), Error> {
assert_eq!(params.mutes.len(), Self::ENTRIES.len());
assert_eq!(old.mutes.len(), Self::ENTRIES.len());
old.mutes
.iter_mut()
.zip(params.mutes.iter())
.zip(Self::ENTRIES)
.filter(|((o, n), _)| !n.eq(o))
.try_for_each(|((old, &new), entry)| {
let &(func_block_id, audio_ch) = entry;
let mut op = AudioFeature::new(
func_block_id,
CtlAttr::Current,
audio_ch,
FeatureCtl::Mute(vec![new]),
);
avc.control(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
.map(|_| *old = new)
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AvcSelectorParameters {
pub selectors: Vec<usize>,
}
pub trait AvcSelectorOperation {
const FUNC_BLOCK_ID_LIST: &'static [u8];
const INPUT_PLUG_ID_LIST: &'static [u8];
fn create_selector_parameters() -> AvcSelectorParameters {
AvcSelectorParameters {
selectors: vec![Default::default(); Self::FUNC_BLOCK_ID_LIST.len()],
}
}
fn cache_selectors(
avc: &BebobAvc,
params: &mut AvcSelectorParameters,
timeout_ms: u32,
) -> Result<(), Error> {
assert_eq!(params.selectors.len(), Self::FUNC_BLOCK_ID_LIST.len());
params
.selectors
.iter_mut()
.zip(Self::FUNC_BLOCK_ID_LIST)
.try_for_each(|(selector, &func_block_id)| {
let mut op = AudioSelector::new(func_block_id, CtlAttr::Current, 0xff);
avc.status(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)?;
Self::INPUT_PLUG_ID_LIST
.iter()
.position(|&input_plug_id| input_plug_id == op.input_plug_id)
.ok_or_else(|| {
let msg = format!(
"Unexpected index of input plug number: {}",
op.input_plug_id
);
Error::new(FileError::Io, &msg)
})
.map(|pos| *selector = pos)
})
}
fn update_selectors(
avc: &BebobAvc,
params: &AvcSelectorParameters,
old: &mut AvcSelectorParameters,
timeout_ms: u32,
) -> Result<(), Error> {
assert_eq!(params.selectors.len(), Self::FUNC_BLOCK_ID_LIST.len());
assert_eq!(old.selectors.len(), Self::FUNC_BLOCK_ID_LIST.len());
old.selectors
.iter_mut()
.zip(params.selectors.iter())
.zip(Self::FUNC_BLOCK_ID_LIST)
.filter(|((o, n), _)| !o.eq(n))
.try_for_each(|((old, &new), &func_block_id)| {
let mut op = AudioSelector::new(func_block_id, CtlAttr::Current, new as u8);
avc.control(&AUDIO_SUBUNIT_0_ADDR, &mut op, timeout_ms)
.map(|_| *old = new)
})
}
}