/*
* Copyright (C) 2005-2007 by Pieter Palmers
* 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 "genericavc/avc_avdevice.h"
#include "libieee1394/configrom.h"
#include "libieee1394/ieee1394service.h"
#include "libavc/avc_definitions.h"
#include "libavc/general/avc_plug_info.h"
#include "libavc/general/avc_extended_plug_info.h"
#include "debugmodule/debugmodule.h"
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
using namespace AVC;
namespace GenericAVC {
IMPL_DEBUG_MODULE( AvDevice, AvDevice, DEBUG_LEVEL_NORMAL );
AvDevice::AvDevice( DeviceManager& d, std::auto_ptr( configRom ))
: FFADODevice( d, configRom )
{
debugOutput( DEBUG_LEVEL_VERBOSE, "Created GenericAVC::AvDevice (NodeID %d)\n",
getConfigRom().getNodeId() );
addOption(Util::OptionContainer::Option("snoopMode",false));
}
bool
AvDevice::probe( ConfigRom& configRom )
{
unsigned int vendorId = configRom.getNodeVendorId();
unsigned int modelId = configRom.getModelId();
GenericAVC::VendorModel vendorModel( SHAREDIR "/ffado_driver_genericavc.txt" );
if ( vendorModel.parse() ) {
return vendorModel.isPresent( vendorId, modelId );
}
return false;
}
FFADODevice *
AvDevice::createDevice(DeviceManager& d, std::auto_ptr( configRom ))
{
return new AvDevice(d, configRom );
}
bool
AvDevice::discover()
{
// check if we already have a valid VendorModel entry
// e.g. because a subclass called this function
if (!GenericAVC::VendorModel::isValid(m_model)) {
unsigned int vendorId = getConfigRom().getNodeVendorId();
unsigned int modelId = getConfigRom().getModelId();
GenericAVC::VendorModel vendorModel( SHAREDIR "/ffado_driver_genericavc.txt" );
if ( vendorModel.parse() ) {
m_model = vendorModel.find( vendorId, modelId );
}
if (!GenericAVC::VendorModel::isValid(m_model)) {
return false;
}
debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n",
m_model.vendor_name.c_str(), m_model.model_name.c_str());
}
if ( !Unit::discover() ) {
debugError( "Could not discover unit\n" );
return false;
}
if((getAudioSubunit( 0 ) == NULL)) {
debugError( "Unit doesn't have an Audio subunit.\n");
return false;
}
if((getMusicSubunit( 0 ) == NULL)) {
debugError( "Unit doesn't have a Music subunit.\n");
return false;
}
return true;
}
void
AvDevice::setVerboseLevel(int l)
{
setDebugLevel(l);
m_pPlugManager->setVerboseLevel(l);
FFADODevice::setVerboseLevel(l);
AVC::Unit::setVerboseLevel(l);
debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l );
}
int
AvDevice::getSamplingFrequency( ) {
AVC::Plug* inputPlug = getPlugById( m_pcrPlugs, Plug::eAPD_Input, 0 );
if ( !inputPlug ) {
debugError( "setSampleRate: Could not retrieve iso input plug 0\n" );
return false;
}
AVC::Plug* outputPlug = getPlugById( m_pcrPlugs, Plug::eAPD_Output, 0 );
if ( !outputPlug ) {
debugError( "setSampleRate: Could not retrieve iso output plug 0\n" );
return false;
}
int samplerate_playback=inputPlug->getSampleRate();
int samplerate_capture=outputPlug->getSampleRate();
if (samplerate_playback != samplerate_capture) {
debugWarning("Samplerates for capture and playback differ!\n");
}
return samplerate_capture;
}
bool
AvDevice::setSamplingFrequency( int s )
{
bool snoopMode=false;
if(!getOption("snoopMode", snoopMode)) {
debugWarning("Could not retrieve snoopMode parameter, defauling to false\n");
}
if(snoopMode) {
int current_sr=getSamplingFrequency();
if (current_sr != s ) {
debugError("In snoop mode it is impossible to set the sample rate.\n");
debugError("Please start the client with the correct setting.\n");
return false;
}
return true;
} else {
AVC::Plug* plug = getPlugById( m_pcrPlugs, Plug::eAPD_Input, 0 );
if ( !plug ) {
debugError( "setSampleRate: Could not retrieve iso input plug 0\n" );
return false;
}
if ( !plug->setSampleRate( s ) )
{
debugError( "setSampleRate: Setting sample rate failed\n" );
return false;
}
plug = getPlugById( m_pcrPlugs, Plug::eAPD_Output, 0 );
if ( !plug ) {
debugError( "setSampleRate: Could not retrieve iso output plug 0\n" );
return false;
}
if ( !plug->setSampleRate( s ) )
{
debugError( "setSampleRate: Setting sample rate failed\n" );
return false;
}
debugOutput( DEBUG_LEVEL_VERBOSE,
"setSampleRate: Set sample rate to %d\n",
s );
return true;
}
// not executable
return false;
}
FFADODevice::ClockSourceVector
AvDevice::getSupportedClockSources() {
FFADODevice::ClockSourceVector r;
PlugVector syncMSUInputPlugs = m_pPlugManager->getPlugsByType(
eST_Music,
0,
0xff,
0xff,
Plug::eAPA_SubunitPlug,
Plug::eAPD_Input,
Plug::eAPT_Sync );
if ( !syncMSUInputPlugs.size() ) {
debugWarning( "No sync input plug for MSU subunit found\n" );
return r;
}
for ( SyncInfoVector::const_iterator it
= getSyncInfos().begin();
it != getSyncInfos().end();
++it )
{
const SyncInfo si=*it;
// check if the destination is a MSU input plug
bool found=false;
for ( PlugVector::const_iterator it2 = syncMSUInputPlugs.begin();
it2 != syncMSUInputPlugs.end();
++it2 )
{
AVC::Plug* msuPlug = *it2;
found |= (msuPlug == si.m_destination);
}
if (found) {
ClockSource s=syncInfoToClockSource(*it);
r.push_back(s);
}
}
return r;
}
bool
AvDevice::setActiveClockSource(ClockSource s) {
Plug *src=m_pPlugManager->getPlug( s.id );
if (!src) {
debugError("Could not find plug with id %d\n", s.id);
return false;
}
for ( SyncInfoVector::const_iterator it
= getSyncInfos().begin();
it != getSyncInfos().end();
++it )
{
const SyncInfo si=*it;
if (si.m_source==src) {
return setActiveSync(si);
}
}
return false;
}
FFADODevice::ClockSource
AvDevice::getActiveClockSource() {
const SyncInfo* si=getActiveSyncInfo();
if ( !si ) {
debugError( "Could not retrieve active sync information\n" );
ClockSource s;
s.type=eCT_Invalid;
return s;
}
debugOutput(DEBUG_LEVEL_VERBOSE, "Active Sync mode: %s\n", si->m_description.c_str() );
return syncInfoToClockSource(*si);
}
FFADODevice::ClockSource
AvDevice::syncInfoToClockSource(const SyncInfo& si) {
ClockSource s;
// the description is easy
// it can be that we overwrite it later
s.description=si.m_description;
// FIXME: always valid at the moment
s.valid=true;
assert(si.m_source);
s.id=si.m_source->getGlobalId();
// now figure out what type this is
switch(si.m_source->getPlugType()) {
case Plug::eAPT_IsoStream:
s.type=eCT_SytMatch;
break;
case Plug::eAPT_Sync:
if(si.m_source->getPlugAddressType() == Plug::eAPA_PCR) {
s.type=eCT_SytStream; // this is logical
} else if(si.m_source->getPlugAddressType() == Plug::eAPA_SubunitPlug) {
s.type=eCT_Internal; // this assumes some stuff
} else if(si.m_source->getPlugAddressType() == Plug::eAPA_ExternalPlug) {
std::string plugname=si.m_source->getName();
s.description=plugname;
// this is basically due to Focusrites interpretation
if(plugname.find( "SPDIF", 0 ) != string::npos) {
s.type=eCT_SPDIF; // this assumes the name will tell us
} else {
s.type=eCT_WordClock; // this assumes a whole lot more
}
} else {
s.type=eCT_Invalid;
}
break;
case Plug::eAPT_Digital:
if(si.m_source->getPlugAddressType() == Plug::eAPA_ExternalPlug) {
std::string plugname=si.m_source->getName();
s.description=plugname;
// this is basically due to Focusrites interpretation
if(plugname.find( "ADAT", 0 ) != string::npos) {
s.type=eCT_ADAT; // this assumes the name will tell us
} else if(plugname.find( "SPDIF", 0 ) != string::npos) {
s.type=eCT_SPDIF; // this assumes the name will tell us
} else {
s.type=eCT_WordClock; // this assumes a whole lot more
}
} else {
s.type=eCT_Invalid;
}
break;
default:
s.type=eCT_Invalid; break;
}
// is it active?
const SyncInfo* active=getActiveSyncInfo();
if (active) {
if ((active->m_source == si.m_source)
&& (active->m_destination == si.m_destination))
s.active=true;
else s.active=false;
} else s.active=false;
return s;
}
bool
AvDevice::lock() {
bool snoopMode=false;
if(!getOption("snoopMode", snoopMode)) {
debugWarning("Could not retrieve snoopMode parameter, defauling to false\n");
}
if (snoopMode) {
// don't lock
} else {
// return Unit::reserve(4);
}
return true;
}
bool
AvDevice::unlock() {
bool snoopMode=false;
if(!getOption("snoopMode", snoopMode)) {
debugWarning("Could not retrieve snoopMode parameter, defauling to false\n");
}
if (snoopMode) {
// don't unlock
} else {
// return Unit::reserve(0);
}
return true;
}
void
AvDevice::showDevice()
{
FFADODevice::showDevice();
AVC::Unit::show();
flushDebugOutput();
}
bool
AvDevice::prepare() {
bool snoopMode=false;
if(!getOption("snoopMode", snoopMode)) {
debugWarning("Could not retrieve snoopMode parameter, defauling to false\n");
}
///////////
// get plugs
AVC::Plug* inputPlug = getPlugById( m_pcrPlugs, Plug::eAPD_Input, 0 );
if ( !inputPlug ) {
debugError( "setSampleRate: Could not retrieve iso input plug 0\n" );
return false;
}
AVC::Plug* outputPlug = getPlugById( m_pcrPlugs, Plug::eAPD_Output, 0 );
if ( !outputPlug ) {
debugError( "setSampleRate: Could not retrieve iso output plug 0\n" );
return false;
}
debugOutput( DEBUG_LEVEL_VERBOSE, "Initializing receive processor...\n");
// create & add streamprocessors
Streaming::StreamProcessor *p;
if ( outputPlug->getNrOfChannels() == 0 ) {
debugError("Receive plug has no channels\n");
return false;
}
p = new Streaming::AmdtpReceiveStreamProcessor(*this,
outputPlug->getNrOfChannels());
if(!p->init()) {
debugFatal("Could not initialize receive processor!\n");
delete p;
return false;
}
if (!addPlugToProcessor(*outputPlug,p,
Streaming::Port::E_Capture)) {
debugFatal("Could not add plug to processor!\n");
delete p;
return false;
}
m_receiveProcessors.push_back(p);
// do the transmit processor
debugOutput( DEBUG_LEVEL_VERBOSE, "Initializing transmit processor%s...\n",
(snoopMode?" in snoop mode":""));
if (snoopMode) {
// we are snooping, so this is receive too.
p=new Streaming::AmdtpReceiveStreamProcessor(*this,
inputPlug->getNrOfChannels());
} else {
p=new Streaming::AmdtpTransmitStreamProcessor(*this,
inputPlug->getNrOfChannels());
}
if(!p->init()) {
debugFatal("Could not initialize transmit processor %s!\n",
(snoopMode?" in snoop mode":""));
delete p;
return false;
}
if (snoopMode) {
if (!addPlugToProcessor(*inputPlug,p,
Streaming::Port::E_Capture)) {
debugFatal("Could not add plug to processor!\n");
return false;
}
} else {
if (!addPlugToProcessor(*inputPlug,p,
Streaming::Port::E_Playback)) {
debugFatal("Could not add plug to processor!\n");
return false;
}
}
// we put this SP into the transmit SP vector,
// no matter if we are in snoop mode or not
// this allows us to find out what direction
// a certain stream should have.
m_transmitProcessors.push_back(p);
return true;
}
bool
AvDevice::addPlugToProcessor(
AVC::Plug& plug,
Streaming::StreamProcessor *processor,
Streaming::AmdtpAudioPort::E_Direction direction) {
std::string id=std::string("dev?");
if(!getOption("id", id)) {
debugWarning("Could not retrieve id parameter, defauling to 'dev?'\n");
}
Plug::ClusterInfoVector& clusterInfos = plug.getClusterInfos();
for ( Plug::ClusterInfoVector::const_iterator it = clusterInfos.begin();
it != clusterInfos.end();
++it )
{
const Plug::ClusterInfo* clusterInfo = &( *it );
Plug::ChannelInfoVector channelInfos = clusterInfo->m_channelInfos;
for ( Plug::ChannelInfoVector::const_iterator it = channelInfos.begin();
it != channelInfos.end();
++it )
{
const Plug::ChannelInfo* channelInfo = &( *it );
std::ostringstream portname;
portname << id << "_" << channelInfo->m_name;
Streaming::Port *p=NULL;
switch(clusterInfo->m_portType) {
case ExtendedPlugInfoClusterInfoSpecificData::ePT_Speaker:
case ExtendedPlugInfoClusterInfoSpecificData::ePT_Headphone:
case ExtendedPlugInfoClusterInfoSpecificData::ePT_Microphone:
case ExtendedPlugInfoClusterInfoSpecificData::ePT_Line:
case ExtendedPlugInfoClusterInfoSpecificData::ePT_Analog:
debugOutput(DEBUG_LEVEL_VERBOSE, " Adding audio channel %s (pos=0x%02X, loc=0x%02X)\n",
channelInfo->m_name.c_str(), channelInfo->m_streamPosition, channelInfo->m_location);
p=new Streaming::AmdtpAudioPort(
portname.str(),
direction,
channelInfo->m_streamPosition,
channelInfo->m_location,
Streaming::AmdtpPortInfo::E_MBLA
);
break;
case ExtendedPlugInfoClusterInfoSpecificData::ePT_MIDI:
debugOutput(DEBUG_LEVEL_VERBOSE, " Adding MIDI channel %s (pos=0x%02X, loc=0x%02X)\n",
channelInfo->m_name.c_str(), channelInfo->m_streamPosition, processor->getPortCount(Streaming::Port::E_Midi));
p=new Streaming::AmdtpMidiPort(
portname.str(),
direction,
channelInfo->m_streamPosition,
// Workaround for out-of-spec hardware
// should be:
// channelInfo->m_location,
// but now we renumber the midi channels' location as they
// are discovered
processor->getPortCount(Streaming::Port::E_Midi),
Streaming::AmdtpPortInfo::E_Midi
);
break;
case ExtendedPlugInfoClusterInfoSpecificData::ePT_SPDIF:
case ExtendedPlugInfoClusterInfoSpecificData::ePT_ADAT:
case ExtendedPlugInfoClusterInfoSpecificData::ePT_TDIF:
case ExtendedPlugInfoClusterInfoSpecificData::ePT_MADI:
case ExtendedPlugInfoClusterInfoSpecificData::ePT_Digital:
debugOutput(DEBUG_LEVEL_VERBOSE, " Adding digital audio channel %s (pos=0x%02X, loc=0x%02X)\n",
channelInfo->m_name.c_str(), channelInfo->m_streamPosition, channelInfo->m_location);
p=new Streaming::AmdtpAudioPort(
portname.str(),
direction,
channelInfo->m_streamPosition,
channelInfo->m_location,
Streaming::AmdtpPortInfo::E_MBLA
);
break;
case ExtendedPlugInfoClusterInfoSpecificData::ePT_NoType:
default:
// unsupported
break;
}
if (!p) {
debugOutput(DEBUG_LEVEL_VERBOSE, "Skipped port %s\n",channelInfo->m_name.c_str());
} else {
if (!processor->addPort(p)) {
debugWarning("Could not register port with stream processor\n");
return false;
}
}
}
}
return true;
}
int
AvDevice::getStreamCount() {
return m_receiveProcessors.size() + m_transmitProcessors.size();
//return 1;
}
Streaming::StreamProcessor *
AvDevice::getStreamProcessorByIndex(int i) {
if (i<(int)m_receiveProcessors.size()) {
return m_receiveProcessors.at(i);
} else if (i<(int)m_receiveProcessors.size() + (int)m_transmitProcessors.size()) {
return m_transmitProcessors.at(i-m_receiveProcessors.size());
}
return NULL;
}
bool
AvDevice::startStreamByIndex(int i) {
int iso_channel=-1;
bool snoopMode=false;
if(!getOption("snoopMode", snoopMode)) {
debugWarning("Could not retrieve snoopMode parameter, defauling to false\n");
}
if (i<(int)m_receiveProcessors.size()) {
int n=i;
Streaming::StreamProcessor *p=m_receiveProcessors.at(n);
if(snoopMode) { // a stream from the device to another host
// FIXME: put this into a decent framework!
// we should check the oPCR[n] on the device
struct iec61883_oPCR opcr;
if (iec61883_get_oPCRX(
get1394Service().getHandle(),
getConfigRom().getNodeId() | 0xffc0,
(quadlet_t *)&opcr,
n)) {
debugWarning("Error getting the channel for SP %d\n",i);
return false;
}
iso_channel=opcr.channel;
} else {
iso_channel=get1394Service().allocateIsoChannelCMP(
getConfigRom().getNodeId() | 0xffc0, n,
get1394Service().getLocalNodeId()| 0xffc0, -1);
}
if (iso_channel<0) {
debugError("Could not allocate ISO channel for SP %d\n",i);
return false;
}
debugOutput(DEBUG_LEVEL_VERBOSE, "Started SP %d on channel %d\n",i,iso_channel);
p->setChannel(iso_channel);
return true;
} else if (i<(int)m_receiveProcessors.size() + (int)m_transmitProcessors.size()) {
int n=i-m_receiveProcessors.size();
Streaming::StreamProcessor *p=m_transmitProcessors.at(n);
if(snoopMode) { // a stream from another host to the device
// FIXME: put this into a decent framework!
// we should check the iPCR[n] on the device
struct iec61883_iPCR ipcr;
if (iec61883_get_iPCRX(
get1394Service().getHandle(),
getConfigRom().getNodeId() | 0xffc0,
(quadlet_t *)&ipcr,
n)) {
debugWarning("Error getting the channel for SP %d\n",i);
return false;
}
iso_channel=ipcr.channel;
} else {
iso_channel=get1394Service().allocateIsoChannelCMP(
get1394Service().getLocalNodeId()| 0xffc0, -1,
getConfigRom().getNodeId() | 0xffc0, n);
}
if (iso_channel<0) {
debugError("Could not allocate ISO channel for SP %d\n",i);
return false;
}
debugOutput(DEBUG_LEVEL_VERBOSE, "Started SP %d on channel %d\n",i,iso_channel);
p->setChannel(iso_channel);
return true;
}
debugError("SP index %d out of range!\n",i);
return false;
}
bool
AvDevice::stopStreamByIndex(int i) {
bool snoopMode=false;
if(!getOption("snoopMode", snoopMode)) {
debugWarning("Could not retrieve snoopMode parameter, defauling to false\n");
}
if (i<(int)m_receiveProcessors.size()) {
int n=i;
Streaming::StreamProcessor *p=m_receiveProcessors.at(n);
if(snoopMode) {
} else {
// deallocate ISO channel
if(!get1394Service().freeIsoChannel(p->getChannel())) {
debugError("Could not deallocate iso channel for SP %d\n",i);
return false;
}
}
p->setChannel(-1);
return true;
} else if (i<(int)m_receiveProcessors.size() + (int)m_transmitProcessors.size()) {
int n=i-m_receiveProcessors.size();
Streaming::StreamProcessor *p=m_transmitProcessors.at(n);
if(snoopMode) {
} else {
// deallocate ISO channel
if(!get1394Service().freeIsoChannel(p->getChannel())) {
debugError("Could not deallocate iso channel for SP %d\n",i);
return false;
}
}
p->setChannel(-1);
return true;
}
debugError("SP index %d out of range!\n",i);
return false;
}
bool
AvDevice::serialize( Glib::ustring basePath, Util::IOSerialize& ser ) const
{
bool result;
result = AVC::Unit::serialize( basePath, ser );
result &= serializeOptions( basePath + "Options", ser );
return result;
}
bool
AvDevice::deserialize( Glib::ustring basePath, Util::IODeserialize& deser )
{
bool result;
result = AVC::Unit::deserialize( basePath, deser );
return result;
}
}