/*
* Copyright (C) 2005-2007 by Daniel Wagner
*
* This file is part of FFADO
* FFADO = Free Firewire (pro-)audio drivers for linux
*
* FFADO is based upon FreeBoB
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "bebob/bebob_functionblock.h"
#include "bebob/bebob_avdevice_subunit.h"
#include "bebob/bebob_avdevice.h"
#include "bebob/bebob_avplug.h"
#include "libieee1394/configrom.h"
#include "libavc/general/avc_plug_info.h"
#include "libavc/streamformat/avc_extended_stream_format.h"
#include "libutil/cmd_serialize.h"
#include
using namespace AVC;
//////////////////////////
BeBoB::SubunitAudio::SubunitAudio( AVC::Unit& avDevice,
subunit_t id )
: AVC::SubunitAudio( avDevice, id )
{
}
BeBoB::SubunitAudio::SubunitAudio()
: AVC::SubunitAudio()
{
}
BeBoB::SubunitAudio::~SubunitAudio()
{
for ( FunctionBlockVector::iterator it = m_functions.begin();
it != m_functions.end();
++it )
{
delete *it;
}
}
AVC::Plug *
BeBoB::SubunitAudio::createPlug( AVC::Unit* unit,
AVC::Subunit* subunit,
AVC::function_block_type_t functionBlockType,
AVC::function_block_type_t functionBlockId,
AVC::Plug::EPlugAddressType plugAddressType,
AVC::Plug::EPlugDirection plugDirection,
AVC::plug_id_t plugId )
{
return new BeBoB::Plug( unit,
subunit,
functionBlockType,
functionBlockId,
plugAddressType,
plugDirection,
plugId );
}
bool
BeBoB::SubunitAudio::discover()
{
debugOutput(DEBUG_LEVEL_NORMAL, "Discovering %s...\n", getName());
// discover the AV/C generic part
if ( !AVC::SubunitAudio::discover() ) {
return false;
}
// do the remaining BeBoB audio subunit discovery
if ( !discoverFunctionBlocks() ) {
debugError( "function block discovering failed\n" );
return false;
}
return true;
}
bool
BeBoB::SubunitAudio::discoverConnections()
{
debugOutput(DEBUG_LEVEL_NORMAL, "Discovering connections...\n");
if ( !Subunit::discoverConnections() ) {
return false;
}
for ( FunctionBlockVector::iterator it = m_functions.begin();
it != m_functions.end();
++it )
{
FunctionBlock* function = *it;
if ( !function->discoverConnections() ) {
debugError( "functionblock connection discovering failed ('%s')\n",
function->getName() );
return false;
}
}
return true;
}
const char*
BeBoB::SubunitAudio::getName()
{
return "BeBoB::AudioSubunit";
}
bool
BeBoB::SubunitAudio::discoverFunctionBlocks()
{
debugOutput( DEBUG_LEVEL_NORMAL,
"Discovering function blocks...\n");
if ( !discoverFunctionBlocksDo(
ExtendedSubunitInfoCmd::eFBT_AudioSubunitSelector) )
{
debugError( "Could not discover function block selector\n" );
return false;
}
if ( !discoverFunctionBlocksDo(
ExtendedSubunitInfoCmd::eFBT_AudioSubunitFeature) )
{
debugError( "Could not discover function block feature\n" );
return false;
}
if ( !discoverFunctionBlocksDo(
ExtendedSubunitInfoCmd::eFBT_AudioSubunitProcessing) )
{
debugError( "Could not discover function block processing\n" );
return false;
}
if ( !discoverFunctionBlocksDo(
ExtendedSubunitInfoCmd::eFBT_AudioSubunitCodec) )
{
debugError( "Could not discover function block codec\n" );
return false;
}
// print a function block list
#ifdef DEBUG
if ((int)getDebugLevel() >= DEBUG_LEVEL_NORMAL) {
for ( FunctionBlockVector::iterator it = m_functions.begin();
it != m_functions.end();
++it )
{
debugOutput(DEBUG_LEVEL_NORMAL, "%20s FB, type 0x%X, id=%d\n",
(*it)->getName(),
(*it)->getType(),
(*it)->getId());
}
}
#endif
return true;
}
bool
BeBoB::SubunitAudio::discoverFunctionBlocksDo(
ExtendedSubunitInfoCmd::EFunctionBlockType fbType )
{
int page = 0;
bool cmdSuccess = false;
bool finished = false;
do {
ExtendedSubunitInfoCmd
extSubunitInfoCmd( m_unit->get1394Service() );
extSubunitInfoCmd.setNodeId( m_unit->getConfigRom().getNodeId() );
extSubunitInfoCmd.setCommandType( AVCCommand::eCT_Status );
extSubunitInfoCmd.setSubunitId( getSubunitId() );
extSubunitInfoCmd.setSubunitType( getSubunitType() );
extSubunitInfoCmd.setVerbose( (int)getDebugLevel() );
extSubunitInfoCmd.m_fbType = fbType;
extSubunitInfoCmd.m_page = page;
cmdSuccess = extSubunitInfoCmd.fire();
if ( cmdSuccess
&& ( extSubunitInfoCmd.getResponse()
== AVCCommand::eR_Implemented ) )
{
for ( ExtendedSubunitInfoPageDataVector::iterator it =
extSubunitInfoCmd.m_infoPageDatas.begin();
cmdSuccess
&& ( it != extSubunitInfoCmd.m_infoPageDatas.end() );
++it )
{
cmdSuccess = createFunctionBlock( fbType, **it );
}
if ( ( extSubunitInfoCmd.m_infoPageDatas.size() != 0 )
&& ( extSubunitInfoCmd.m_infoPageDatas.size() == 5 ) )
{
page++;
} else {
finished = true;
}
} else {
finished = true;
}
} while ( cmdSuccess && !finished );
return cmdSuccess;
}
bool
BeBoB::SubunitAudio::createFunctionBlock(
ExtendedSubunitInfoCmd::EFunctionBlockType fbType,
ExtendedSubunitInfoPageData& data )
{
FunctionBlock::ESpecialPurpose purpose
= convertSpecialPurpose( data.m_functionBlockSpecialPupose );
FunctionBlock* fb = 0;
switch ( fbType ) {
case ExtendedSubunitInfoCmd::eFBT_AudioSubunitSelector:
{
fb = new FunctionBlockSelector( *this,
data.m_functionBlockId,
purpose,
data.m_noOfInputPlugs,
data.m_noOfOutputPlugs,
(int)getDebugLevel() );
}
break;
case ExtendedSubunitInfoCmd::eFBT_AudioSubunitFeature:
{
fb = new FunctionBlockFeature( *this,
data.m_functionBlockId,
purpose,
data.m_noOfInputPlugs,
data.m_noOfOutputPlugs,
(int)getDebugLevel() );
}
break;
case ExtendedSubunitInfoCmd::eFBT_AudioSubunitProcessing:
{
switch ( data.m_functionBlockType ) {
case ExtendedSubunitInfoCmd::ePT_EnhancedMixer:
{
fb = new FunctionBlockEnhancedMixer( *this,
data.m_functionBlockId,
purpose,
data.m_noOfInputPlugs,
data.m_noOfOutputPlugs,
(int)getDebugLevel() );
}
break;
case ExtendedSubunitInfoCmd::ePT_Mixer:
case ExtendedSubunitInfoCmd::ePT_Generic:
case ExtendedSubunitInfoCmd::ePT_UpDown:
case ExtendedSubunitInfoCmd::ePT_DolbyProLogic:
case ExtendedSubunitInfoCmd::ePT_3DStereoExtender:
case ExtendedSubunitInfoCmd::ePT_Reverberation:
case ExtendedSubunitInfoCmd::ePT_Chorus:
case ExtendedSubunitInfoCmd::ePT_DynamicRangeCompression:
default:
fb = new FunctionBlockProcessing( *this,
data.m_functionBlockId,
purpose,
data.m_noOfInputPlugs,
data.m_noOfOutputPlugs,
(int)getDebugLevel() );
debugWarning( "Dummy function block processing created. "
"Implementation is missing\n" );
}
}
break;
case ExtendedSubunitInfoCmd::eFBT_AudioSubunitCodec:
{
fb = new FunctionBlockCodec( *this,
data.m_functionBlockId,
purpose,
data.m_noOfInputPlugs,
data.m_noOfOutputPlugs,
(int)getDebugLevel() );
debugWarning( "Dummy function block codec created. "
"Implementation is missing\n" );
}
break;
default:
debugError( "Unhandled function block type found\n" );
return false;
}
if ( !fb ) {
debugError( "Could create function block\n" );
return false;
}
if ( !fb->discover() ) {
debugError( "Could not discover function block %s\n",
fb->getName() );
delete fb;
return false;
}
m_functions.push_back( fb );
return true;
}
BeBoB::FunctionBlock::ESpecialPurpose
BeBoB::SubunitAudio::convertSpecialPurpose(
function_block_special_purpose_t specialPurpose )
{
FunctionBlock::ESpecialPurpose p;
switch ( specialPurpose ) {
case ExtendedSubunitInfoPageData::eSP_InputGain:
p = FunctionBlock::eSP_InputGain;
break;
case ExtendedSubunitInfoPageData::eSP_OutputVolume:
p = FunctionBlock::eSP_OutputVolume;
break;
default:
p = FunctionBlock::eSP_NoSpecialPurpose;
}
return p;
}
bool
BeBoB::SubunitAudio::serializeChild( Glib::ustring basePath,
Util::IOSerialize& ser ) const
{
bool result = true;
int i = 0;
for ( FunctionBlockVector::const_iterator it = m_functions.begin();
it != m_functions.end();
++it )
{
FunctionBlock* pFB = *it;
std::ostringstream strstrm;
strstrm << basePath << "FunctionBlock" << i << "/";
result &= pFB->serialize( strstrm.str() , ser );
i++;
}
return result;
}
bool
BeBoB::SubunitAudio::deserializeChild( Glib::ustring basePath,
Util::IODeserialize& deser,
AVC::Unit& avDevice )
{
int i = 0;
bool bFinished = false;
do {
std::ostringstream strstrm;
strstrm << basePath << "FunctionBlock" << i << "/";
FunctionBlock* pFB = FunctionBlock::deserialize( strstrm.str(),
deser,
avDevice,
*this );
if ( pFB ) {
m_functions.push_back( pFB );
i++;
} else {
bFinished = true;
}
} while ( !bFinished );
return true;
}
////////////////////////////////////////////
BeBoB::SubunitMusic::SubunitMusic( AVC::Unit& avDevice,
subunit_t id )
: AVC::SubunitMusic( avDevice, id )
{
}
BeBoB::SubunitMusic::SubunitMusic()
: AVC::SubunitMusic()
{
}
BeBoB::SubunitMusic::~SubunitMusic()
{
}
AVC::Plug *
BeBoB::SubunitMusic::createPlug( AVC::Unit* unit,
AVC::Subunit* subunit,
AVC::function_block_type_t functionBlockType,
AVC::function_block_type_t functionBlockId,
AVC::Plug::EPlugAddressType plugAddressType,
AVC::Plug::EPlugDirection plugDirection,
AVC::plug_id_t plugId )
{
return new BeBoB::Plug( unit,
subunit,
functionBlockType,
functionBlockId,
plugAddressType,
plugDirection,
plugId );
}
bool
BeBoB::SubunitMusic::discover()
{
debugOutput(DEBUG_LEVEL_NORMAL, "Discovering %s...\n", getName());
// discover the AV/C generic part
if ( !AVC::SubunitMusic::discover() ) {
return false;
}
// do the remaining BeBoB music subunit discovery
// which is nothing
return true;
}
const char*
BeBoB::SubunitMusic::getName()
{
return "BeBoB::MusicSubunit";
}
bool
BeBoB::SubunitMusic::serializeChild( Glib::ustring basePath,
Util::IOSerialize& ser ) const
{
return true;
}
bool
BeBoB::SubunitMusic::deserializeChild( Glib::ustring basePath,
Util::IODeserialize& deser,
AVC::Unit& avDevice )
{
return true;
}