Index: /branches/libffado-2.0/external/SConscript =================================================================== --- /branches/libffado-2.0/external/SConscript (revision 1185) +++ /branches/libffado-2.0/external/SConscript (revision 1185) @@ -0,0 +1,29 @@ +# +# Copyright (C) 2007-2008 Arnold Krille +# Copyright (C) 2007-2008 Pieter Palmers +# +# 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 2 of the License, or +# (at your option) version 3 of the License. +# +# 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 . +# + +Import( 'env' ) + +env = env.Clone() + +env.SConscript( dirs="dbus", exports="env" ) + Index: /branches/libffado-2.0/external/dbus/tools/introspect.h =================================================================== --- /branches/libffado-2.0/external/dbus/tools/introspect.h (revision 562) +++ /branches/libffado-2.0/external/dbus/tools/introspect.h (revision 562) @@ -0,0 +1,44 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_TOOLS_INTROSPECT_H +#define __DBUSXX_TOOLS_INTROSPECT_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +class IntrospectedObject : public DBus::IntrospectableProxy, public DBus::ObjectProxy +{ +public: + + IntrospectedObject( DBus::Connection& conn, const char* path, const char* service ) + : DBus::ObjectProxy(conn, path, service) + {} +}; + +#endif//__DBUSXX_TOOLS_INTROSPECT_H Index: /branches/libffado-2.0/external/dbus/tools/xml.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/tools/xml.cpp (revision 562) +++ /branches/libffado-2.0/external/dbus/tools/xml.cpp (revision 562) @@ -0,0 +1,315 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include "xml.h" + +#include + +#include + +std::istream& operator >> ( std::istream& in, DBus::Xml::Document& doc ) +{ + std::stringbuf xmlbuf; + in.get(xmlbuf, '\0'); + doc.from_xml(xmlbuf.str()); + + return in; +} + +std::ostream& operator << ( std::ostream& out, const DBus::Xml::Document& doc ) +{ + return out << doc.to_xml(); +} + +using namespace DBus; +using namespace DBus::Xml; + +Error::Error( const char* error, int line, int column ) +{ + std::ostringstream estream; + + estream << "line " << line << ", column " << column << ": " << error; + + _error = estream.str(); +} + +Node::Node( const char* n, const char** a ) +: name(n) +{ + if(a) + for(int i = 0; a[i]; i += 2) + { + _attrs[a[i]] = a[i+1]; + + //debug_log("xml:\t%s = %s", a[i], a[i+1]); + } +} + +Nodes Nodes::operator[]( const std::string& key ) +{ + Nodes result; + + for(iterator i = begin(); i != end(); ++i) + { + Nodes part = (**i)[key]; + + result.insert(result.end(), part.begin(), part.end()); + } + return result; +} + +Nodes Nodes::select( const std::string& attr, const std::string& value ) +{ + Nodes result; + + for(iterator i = begin(); i != end(); ++i) + { + if((*i)->get(attr) == value) + result.insert(result.end(), *i); + } + return result; +} + +Nodes Node::operator[]( const std::string& key ) +{ + Nodes result; + + if(key.length() == 0) return result; + + for(Children::iterator i = children.begin(); i != children.end(); ++i) + { + if(i->name == key) + result.push_back(&(*i)); + } + return result; +} + +std::string Node::get( const std::string& attribute ) +{ + if(_attrs.find(attribute) != _attrs.end()) + return _attrs[attribute]; + else + return ""; +} + +void Node::set( const std::string& attribute, std::string value ) +{ + if(value.length()) + _attrs[attribute] = value; + else + _attrs.erase(value); +} + +std::string Node::to_xml() const +{ + std::string xml; + int depth = 0; + + _raw_xml(xml, depth); + + return xml; +} + +void Node::_raw_xml( std::string& xml, int& depth ) const +{ + xml.append(depth*2, ' '); + xml.append("<"+name); + + for(Attributes::const_iterator i = _attrs.begin(); i != _attrs.end(); ++i) + { + xml.append(" "+i->first+"=\""+i->second+"\""); + } + + if(cdata.length() == 0 && children.size() == 0) + { + xml.append("/>\n"); + } + else + { + xml.append(">"); + + if(cdata.length()) + { + xml.append(cdata); + } + + if(children.size()) + { + xml.append("\n"); + depth++; + + for(Children::const_iterator i = children.begin(); i != children.end(); ++i) + { + i->_raw_xml(xml, depth); + } + + depth--; + xml.append(depth*2, ' '); + } + xml.append("\n"); + } +} + +Document::Document() +: root(0), _depth(0) +{ +} + +Document::Document( const std::string& xml ) +: root(0), _depth(0) +{ + from_xml(xml); +} + +Document::~Document() +{ + delete root; +} + +struct Document::Expat +{ + static void start_doctype_decl_handler( + void* data, const XML_Char* name, const XML_Char* sysid, const XML_Char* pubid, int has_internal_subset + ); + static void end_doctype_decl_handler( void* data ); + static void start_element_handler( void *data, const XML_Char *name, const XML_Char **atts ); + static void character_data_handler( void *data, const XML_Char* chars, int len ); + static void end_element_handler( void *data, const XML_Char *name ); +}; + +void Document::from_xml( const std::string& xml ) +{ + _depth = 0; + delete root; + root = 0; + + XML_Parser parser = XML_ParserCreate("UTF-8"); + + XML_SetUserData(parser, this); + + XML_SetDoctypeDeclHandler( + parser, + Document::Expat::start_doctype_decl_handler, + Document::Expat::end_doctype_decl_handler + ); + + XML_SetElementHandler( + parser, + Document::Expat::start_element_handler, + Document::Expat::end_element_handler + ); + + XML_SetCharacterDataHandler( + parser, + Document::Expat::character_data_handler + ); + + XML_Status status = XML_Parse(parser, xml.c_str(), xml.length(), true); + + if(status == XML_STATUS_ERROR) + { + const char* error = XML_ErrorString(XML_GetErrorCode(parser)); + int line = XML_GetCurrentLineNumber(parser); + int column = XML_GetCurrentColumnNumber(parser); + + XML_ParserFree(parser); + + throw Error(error, line, column); + } + else + { + XML_ParserFree(parser); + } +} + +std::string Document::to_xml() const +{ + return root->to_xml(); +} + +void Document::Expat::start_doctype_decl_handler( + void* data, const XML_Char* name, const XML_Char* sysid, const XML_Char* pubid, int has_internal_subset +) +{ +} + +void Document::Expat::end_doctype_decl_handler( void* data ) +{ +} + +void Document::Expat::start_element_handler( void *data, const XML_Char *name, const XML_Char **atts ) +{ + Document* doc = (Document*)data; + + //debug_log("xml:%d -> %s", doc->_depth, name); + + if(!doc->root) + { + doc->root = new Node(name, atts); + } + else + { + Node::Children* cld = &(doc->root->children); + + for(int i = 1; i < doc->_depth; ++i) + { + cld = &(cld->back().children); + } + cld->push_back(Node(name, atts)); + + //std::cerr << doc->to_xml() << std::endl; + } + doc->_depth++; +} + +void Document::Expat::character_data_handler( void *data, const XML_Char* chars, int len ) +{ + Document* doc = (Document*)data; + + Node* nod = doc->root; + + for(int i = 1; i < doc->_depth; ++i) + { + nod = &(nod->children.back()); + } + int x, y; + + x = 0; + y = len-1; + + while(isspace(chars[y]) && y > 0) --y; + while(isspace(chars[x]) && x < y) ++x; + + nod->cdata = std::string(chars, x, y+1); +} + +void Document::Expat::end_element_handler( void *data, const XML_Char *name ) +{ + Document* doc = (Document*)data; + + //debug_log("xml:%d <- %s", doc->_depth, name); + + doc->_depth--; +} + Index: /branches/libffado-2.0/external/dbus/tools/xml.h =================================================================== --- /branches/libffado-2.0/external/dbus/tools/xml.h (revision 562) +++ /branches/libffado-2.0/external/dbus/tools/xml.h (revision 562) @@ -0,0 +1,142 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_XML_H +#define __DBUSXX_XML_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +namespace DBus { + +namespace Xml { + +class Error : public std::exception +{ +public: + + Error( const char* error, int line, int column ); + + ~Error() throw() + {} + + const char* what() const throw() + { + return _error.c_str(); + } + +private: + + std::string _error; +}; + +class Node; + +class Nodes : public std::vector +{ +public: + + Nodes operator[]( const std::string& key ); + + Nodes select( const std::string& attr, const std::string& value ); +}; + +class Node +{ +public: + + typedef std::map Attributes; + + typedef std::vector Children; + + std::string name; + std::string cdata; + Children children; + + Node( std::string& n, Attributes& a ) + : name(n), _attrs(a) + {} + + Node( const char* n, const char** a = NULL ); + + Nodes operator[]( const std::string& key ); + + std::string get( const std::string& attribute ); + + void set( const std::string& attribute, std::string value ); + + std::string to_xml() const; + + Node& add( Node child ) + { + children.push_back(child); + return children.back(); + } + +private: + + void _raw_xml( std::string& xml, int& depth ) const; + + Attributes _attrs; +}; + +class Document +{ +public: + + struct Expat; + + Node* root; + + Document(); + + Document( const std::string& xml ); + + ~Document(); + + void from_xml( const std::string& xml ); + + std::string to_xml() const; + +private: + + int _depth; +}; + +} /* namespace Xml */ + +} /* namespace DBus */ + +std::istream& operator >> ( std::istream&, DBus::Xml::Document& ); +std::ostream& operator << ( std::ostream&, DBus::Xml::Document& ); + +#endif//__DBUSXX_XML_H Index: /branches/libffado-2.0/external/dbus/tools/xml2cpp.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/tools/xml2cpp.cpp (revision 962) +++ /branches/libffado-2.0/external/dbus/tools/xml2cpp.cpp (revision 962) @@ -0,0 +1,973 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include "xml2cpp.h" + +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace DBus; + +static const char* tab = " "; + +static const char* header = "\n\ +/*\n\ + * This file was automatically generated by dbusxx-xml2cpp; DO NOT EDIT!\n\ + */\n\ +\n\ +"; + +static const char* dbus_includes = "\n\ +#include \n\ +\n\ +"; + +typedef map TypeCache; + +void usage( const char* argv0 ) +{ + cerr << endl << "Usage: " << argv0 << " [ --proxy= ] [ --adaptor= ]" + << endl << endl; + exit(-1); +} + +void underscorize( string& str ) +{ + for(unsigned int i = 0; i < str.length(); ++i) + { + if(!isalpha(str[i]) && !isdigit(str[i])) str[i] = '_'; + } +} + +string stub_name( string name ) +{ + underscorize(name); + + return "_" + name + "_stub"; +} + +int char_to_atomic_type( char t ) +{ + if(strchr("ybnqiuxtdsgavre", t)) + return t; + + return DBUS_TYPE_INVALID; +} + +const char* atomic_type_to_string( char t ) +{ + static struct { const char type; const char *name; } atos[] = + { + { 'y', "::DBus::Byte" }, + { 'b', "::DBus::Bool" }, + { 'n', "::DBus::Int16" }, + { 'q', "::DBus::UInt16" }, + { 'i', "::DBus::Int32" }, + { 'u', "::DBus::UInt32" }, + { 'x', "::DBus::Int64" }, + { 't', "::DBus::UInt64" }, + { 'd', "::DBus::Double" }, + { 's', "::DBus::String" }, + { 'o', "::DBus::Path" }, + { 'g', "::DBus::Signature" }, + { 'v', "::DBus::Variant" }, + { '\0', "" } + }; + int i; + + for(i = 0; atos[i].type; ++i) + { + if(atos[i].type == t) break; + } + return atos[i].name; +} + +bool is_atomic_type( const string& type ) +{ + return type.length() == 1 && char_to_atomic_type(type[0]) != DBUS_TYPE_INVALID; +} + +void _parse_signature( const string& signature, string& type, unsigned int& i ) +{ + for(; i < signature.length(); ++i) + { + switch(signature[i]) + { + case 'a': + { + switch(signature[++i]) + { + case '{': + { + type += "std::map< "; + + const char* atom = atomic_type_to_string(signature[++i]); + if(!atom) + { + cerr << "invalid signature" << endl; + exit(-1); + } + type += atom; + type += ", "; + ++i; + break; + } + default: + { + type += "std::vector< "; + break; + } + } + _parse_signature(signature, type, i); + type += " >"; + continue; + } + case '(': + { + type += "::DBus::Struct< "; + ++i; + _parse_signature(signature, type, i); + type += " >"; + continue; + } + case ')': + case '}': + { + return; + } + default: + { + const char* atom = atomic_type_to_string(signature[i]); + if(!atom) + { + cerr << "invalid signature" << endl; + exit(-1); + } + type += atom; + + if(signature[i+1] != ')' && signature[i+1] != '}' && i+1 < signature.length()) + { + type += ", "; + } + break; + } + } + } +} + +string signature_to_type( const string& signature ) +{ + string type; + unsigned int i = 0; + _parse_signature(signature, type, i); + return type; +} + +void generate_proxy( Xml::Document& doc, const char* filename ) +{ + cerr << "writing " << filename << endl; + + ofstream file(filename); + if(file.bad()) + { + cerr << "unable to write file " << filename << endl; + exit(-1); + } + + file << header; + string filestring = filename; + underscorize(filestring); + + string cond_comp = "__dbusxx__" + filestring + "__PROXY_MARSHAL_H"; + + file << "#ifndef " << cond_comp << endl; + file << "#define " << cond_comp << endl; + + file << dbus_includes; + + Xml::Node& root = *(doc.root); + Xml::Nodes interfaces = root["interface"]; + + for(Xml::Nodes::iterator i = interfaces.begin(); i != interfaces.end(); ++i) + { + Xml::Node& iface = **i; + Xml::Nodes methods = iface["method"]; + Xml::Nodes signals = iface["signal"]; + Xml::Nodes properties = iface["property"]; + Xml::Nodes ms; + ms.insert(ms.end(), methods.begin(), methods.end()); + ms.insert(ms.end(), signals.begin(), signals.end()); + + string ifacename = iface.get("name"); + if(ifacename == "org.freedesktop.DBus.Introspectable" + ||ifacename == "org.freedesktop.DBus.Properties") + { + cerr << "skipping interface " << ifacename << endl; + continue; + } + + istringstream ss(ifacename); + string nspace; + unsigned int nspaces = 0; + + while(ss.str().find('.', ss.tellg()) != string::npos) + { + getline(ss, nspace, '.'); + + file << "namespace " << nspace << " {" << endl; + + ++nspaces; + } + file << endl; + + string ifaceclass; + + getline(ss, ifaceclass); + + cerr << "generating code for interface " << ifacename << "..." << endl; + + file << "class " << ifaceclass << endl + << " : public ::DBus::InterfaceProxy" << endl + << "{" << endl + << "public:" << endl + << endl + << tab << ifaceclass << "()" << endl + << tab << ": ::DBus::InterfaceProxy(\"" << ifacename << "\")" << endl + << tab << "{" << endl; + + for(Xml::Nodes::iterator si = signals.begin(); si != signals.end(); ++si) + { + Xml::Node& signal = **si; + + string marshname = "_" + signal.get("name") + "_stub"; + + file << tab << tab << "connect_signal(" + << ifaceclass << ", " << signal.get("name") << ", " << stub_name(signal.get("name")) + << ");" << endl; + } + + file << tab << "}" << endl + << endl; + + file << "public:" << endl + << endl + << tab << "/* methods exported by this interface," << endl + << tab << " * this functions will invoke the corresponding methods on the remote objects" << endl + << tab << " */" << endl; + + for(Xml::Nodes::iterator mi = methods.begin(); mi != methods.end(); ++mi) + { + Xml::Node& method = **mi; + Xml::Nodes args = method["arg"]; + Xml::Nodes args_in = args.select("direction","in"); + Xml::Nodes args_out = args.select("direction","out"); + + if(args_out.size() == 0 || args_out.size() > 1 ) + { + file << tab << "void "; + } + else if(args_out.size() == 1) + { + file << tab << signature_to_type(args_out.front()->get("type")) << " "; + } + + file << method.get("name") << "( "; + + unsigned int i = 0; + for(Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++i) + { + Xml::Node& arg = **ai; + file << "const " << signature_to_type(arg.get("type")) << "& "; + + string arg_name = arg.get("name"); + if(arg_name.length()) + file << arg_name; + else + file << "argin" << i; + + if((i+1 != args_in.size() || args_out.size() > 1)) + file << ", "; + } + + if(args_out.size() > 1) + { + unsigned int i = 0; + for(Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++i) + { + Xml::Node& arg = **ao; + file << signature_to_type(arg.get("type")) << "&"; + + string arg_name = arg.get("name"); + if(arg_name.length()) + file << " " << arg_name; + else + file << " argout" << i; + + if(i+1 != args_out.size()) + file << ", "; + } + } + file << " )" << endl; + + file << tab << "{" << endl + << tab << tab << "::DBus::CallMessage call;" << endl; + + if(args_in.size() > 0) + { + file << tab << tab << "::DBus::MessageIter wi = call.writer();" << endl + << endl; + } + + unsigned int j = 0; + for(Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++j) + { + Xml::Node& arg = **ai; + string arg_name = arg.get("name"); + if(arg_name.length()) + file << tab << tab << "wi << " << arg_name << ";" << endl; + else + file << tab << tab << "wi << argin" << j << ";" << endl; + } + + file << tab << tab << "call.member(\"" << method.get("name") << "\");" << endl + << tab << tab << "::DBus::Message ret = invoke_method(call);" << endl; + + + if(args_out.size() > 0) + { + file << tab << tab << "::DBus::MessageIter ri = ret.reader();" << endl + << endl; + } + + if(args_out.size() == 1) + { + file << tab << tab << signature_to_type(args_out.front()->get("type")) << " argout;" << endl; + file << tab << tab << "ri >> argout;" << endl; + file << tab << tab << "return argout;" << endl; + } + else if(args_out.size() > 1) + { + unsigned int i = 0; + for(Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++i) + { + Xml::Node& arg = **ao; + + string arg_name = arg.get("name"); + if(arg_name.length()) + file << tab << tab << "ri >> " << arg.get("name") << ";" << endl; + else + file << tab << tab << "ri >> argout" << i << ";" << endl; + } + } + + file << tab << "}" << endl + << endl; + } + + file << endl + << "public:" << endl + << endl + << tab << "/* signal handlers for this interface" << endl + << tab << " */" << endl; + + for(Xml::Nodes::iterator si = signals.begin(); si != signals.end(); ++si) + { + Xml::Node& signal = **si; + Xml::Nodes args = signal["arg"]; + + file << tab << "virtual void " << signal.get("name") << "( "; + + unsigned int i = 0; + for(Xml::Nodes::iterator ai = args.begin(); ai != args.end(); ++ai, ++i) + { + Xml::Node& arg = **ai; + file << "const " << signature_to_type(arg.get("type")) << "& "; + + string arg_name = arg.get("name"); + if(arg_name.length()) + file << arg_name; + else + file << "argin" << i; + + if((ai+1 != args.end())) + file << ", "; + } + file << " ) = 0;" << endl; + } + + file << endl + << "private:" << endl + << endl + << tab << "/* unmarshalers (to unpack the DBus message before calling the actual signal handler)" << endl + << tab << " */" << endl; + + for(Xml::Nodes::iterator si = signals.begin(); si != signals.end(); ++si) + { + Xml::Node& signal = **si; + Xml::Nodes args = signal["arg"]; + + file << tab << "void " << stub_name(signal.get("name")) << "( const ::DBus::SignalMessage& sig )" << endl + << tab << "{" << endl; + + if(args.size() > 0) + { + file << tab << tab << "::DBus::MessageIter ri = sig.reader();" << endl + << endl; + } + + unsigned int i = 0; + for(Xml::Nodes::iterator ai = args.begin(); ai != args.end(); ++ai, ++i) + { + Xml::Node& arg = **ai; + file << tab << tab << signature_to_type(arg.get("type")) << " " ; + + string arg_name = arg.get("name"); + if(arg_name.length()) + file << arg_name << ";" << " ri >> " << arg_name << ";" << endl; + else + file << "arg" << i << ";" << " ri >> " << "arg" << i << ";" << endl; + } + + file << tab << tab << signal.get("name") << "("; + + unsigned int j = 0; + for(Xml::Nodes::iterator ai = args.begin(); ai != args.end(); ++ai, ++j) + { + Xml::Node& arg = **ai; + + string arg_name = arg.get("name"); + if(arg_name.length()) + file << arg_name; + else + file << "arg" << j; + + if(ai+1 != args.end()) + file << ", "; + } + + file << ");" << endl; + + file << tab << "}" << endl; + } + + + file << "};" << endl + << endl; + + for(unsigned int i = 0; i < nspaces; ++i) + { + file << "} "; + } + file << endl; + } + + file << "#endif//" << cond_comp << endl; + + file.close(); +} + +void generate_adaptor( Xml::Document& doc, const char* filename ) +{ + cerr << "writing " << filename << endl; + + ofstream file(filename); + if(file.bad()) + { + cerr << "unable to write file " << filename << endl; + exit(-1); + } + + file << header; + string filestring = filename; + underscorize(filestring); + + string cond_comp = "__dbusxx__" + filestring + "__ADAPTOR_MARSHAL_H"; + + file << "#ifndef " << cond_comp << endl + << "#define " << cond_comp << endl; + + file << dbus_includes; + + Xml::Node& root = *(doc.root); + Xml::Nodes interfaces = root["interface"]; + + for(Xml::Nodes::iterator i = interfaces.begin(); i != interfaces.end(); ++i) + { + Xml::Node& iface = **i; + Xml::Nodes methods = iface["method"]; + Xml::Nodes signals = iface["signal"]; + Xml::Nodes properties = iface["property"]; + Xml::Nodes ms; + ms.insert(ms.end(), methods.begin(), methods.end()); + ms.insert(ms.end(), signals.begin(), signals.end()); + + string ifacename = iface.get("name"); + if(ifacename == "org.freedesktop.DBus.Introspectable" + ||ifacename == "org.freedesktop.DBus.Properties") + { + cerr << "skipping interface " << ifacename << endl; + continue; + } + + istringstream ss(ifacename); + string nspace; + unsigned int nspaces = 0; + + while(ss.str().find('.', ss.tellg()) != string::npos) + { + getline(ss, nspace, '.'); + + file << "namespace " << nspace << " {" << endl; + + ++nspaces; + } + file << endl; + + string ifaceclass; + + getline(ss, ifaceclass); + + cerr << "generating code for interface " << ifacename << "..." << endl; + + file << "class " << ifaceclass << endl + << ": public ::DBus::InterfaceAdaptor" << endl + << "{" << endl + << "public:" << endl + << endl + << tab << ifaceclass << "()" << endl + << tab << ": ::DBus::InterfaceAdaptor(\"" << ifacename << "\")" << endl + << tab << "{" << endl; + + for(Xml::Nodes::iterator pi = properties.begin(); pi != properties.end(); ++pi) + { + Xml::Node& property = **pi; + + file << tab << tab << "bind_property(" + << property.get("name") << ", " + << "\"" << property.get("type") << "\", " + << ( property.get("access").find("read") != string::npos + ? "true" + : "false" ) + << ", " + << ( property.get("access").find("write") != string::npos + ? "true" + : "false" ) + << ");" << endl; + } + + for(Xml::Nodes::iterator mi = methods.begin(); mi != methods.end(); ++mi) + { + Xml::Node& method = **mi; + + file << tab << tab << "register_method(" + << ifaceclass << ", " << method.get("name") << ", "<< stub_name(method.get("name")) + << ");" << endl; + } + + file << tab << "}" << endl + << endl; + + file << tab << "::DBus::IntrospectedInterface* const introspect() const " << endl + << tab << "{" << endl; + + for(Xml::Nodes::iterator mi = ms.begin(); mi != ms.end(); ++mi) + { + Xml::Node& method = **mi; + Xml::Nodes args = method["arg"]; + + file << tab << tab << "static ::DBus::IntrospectedArgument " << method.get("name") << "_args[] = " << endl + << tab << tab << "{" << endl; + + for(Xml::Nodes::iterator ai = args.begin(); ai != args.end(); ++ai) + { + Xml::Node& arg = **ai; + + file << tab << tab << tab << "{ "; + + if(arg.get("name").length()) + { + file << "\"" << arg.get("name") << "\", "; + } + else + { + file << "0, "; + } + file << "\"" << arg.get("type") << "\", " + << ( arg.get("direction") == "in" ? "true" : "false" ) + << " }," << endl; + } + file << tab << tab << tab << "{ 0, 0, 0 }" << endl + << tab << tab << "};" << endl; + } + + file << tab << tab << "static ::DBus::IntrospectedMethod " << ifaceclass << "_methods[] = " << endl + << tab << tab << "{" << endl; + + for(Xml::Nodes::iterator mi = methods.begin(); mi != methods.end(); ++mi) + { + Xml::Node& method = **mi; + + file << tab << tab << tab << "{ \"" << method.get("name") << "\", " << method.get("name") << "_args }," << endl; + } + + file << tab << tab << tab << "{ 0, 0 }" << endl + << tab << tab << "};" << endl; + + file << tab << tab << "static ::DBus::IntrospectedMethod " << ifaceclass << "_signals[] = " << endl + << tab << tab << "{" << endl; + + for(Xml::Nodes::iterator si = signals.begin(); si != signals.end(); ++si) + { + Xml::Node& method = **si; + + file << tab << tab << tab << "{ \"" << method.get("name") << "\", " << method.get("name") << "_args }," << endl; + } + + file << tab << tab << tab << "{ 0, 0 }" << endl + << tab << tab << "};" << endl; + + file << tab << tab << "static ::DBus::IntrospectedProperty " << ifaceclass << "_properties[] = " << endl + << tab << tab << "{" << endl; + + for(Xml::Nodes::iterator pi = properties.begin(); pi != properties.end(); ++pi) + { + Xml::Node& property = **pi; + + file << tab << tab << tab << "{ " + << "\"" << property.get("name") << "\", " + << "\"" << property.get("type") << "\", " + << ( property.get("access").find("read") != string::npos + ? "true" + : "false" ) + << ", " + << ( property.get("access").find("write") != string::npos + ? "true" + : "false" ) + << " }," << endl; + } + + + file << tab << tab << tab << "{ 0, 0, 0, 0 }" << endl + << tab << tab << "};" << endl; + + file << tab << tab << "static ::DBus::IntrospectedInterface " << ifaceclass << "_interface = " << endl + << tab << tab << "{" << endl + << tab << tab << tab << "\"" << ifacename << "\"," << endl + << tab << tab << tab << ifaceclass << "_methods," << endl + << tab << tab << tab << ifaceclass << "_signals," << endl + << tab << tab << tab << ifaceclass << "_properties" << endl + << tab << tab << "};" << endl + << tab << tab << "return &" << ifaceclass << "_interface;" << endl + << tab << "}" << endl + << endl; + + file << "public:" << endl + << endl + << tab << "/* properties exposed by this interface, use" << endl + << tab << " * property() and property(value) to get and set a particular property" << endl + << tab << " */" << endl; + + for(Xml::Nodes::iterator pi = properties.begin(); pi != properties.end(); ++pi) + { + Xml::Node& property = **pi; + string name = property.get("name"); + string type = property.get("type"); + string type_name = signature_to_type(type); + + file << tab << "::DBus::PropertyAdaptor< " << type_name << " > " << name << ";" << endl; + } + + file << endl; + + file << "public:" << endl + << endl + << tab << "/* methods exported by this interface," << endl + << tab << " * you will have to implement them in your ObjectAdaptor" << endl + << tab << " */" << endl; + + for(Xml::Nodes::iterator mi = methods.begin(); mi != methods.end(); ++mi) + { + Xml::Node& method = **mi; + Xml::Nodes args = method["arg"]; + Xml::Nodes args_in = args.select("direction","in"); + Xml::Nodes args_out = args.select("direction","out"); + + file << tab << "virtual "; + + if(args_out.size() == 0 || args_out.size() > 1 ) + { + file << "void "; + } + else if(args_out.size() == 1) + { + file << signature_to_type(args_out.front()->get("type")) << " "; + } + + file << method.get("name") << "( "; + + unsigned int i = 0; + for(Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++i) + { + Xml::Node& arg = **ai; + file << "const " << signature_to_type(arg.get("type")) << "& "; + + string arg_name = arg.get("name"); + if(arg_name.length()) + file << arg_name; + + if((i+1 != args_in.size() || args_out.size() > 1)) + file << ", "; + } + + if(args_out.size() > 1) + { + unsigned int i = 0; + for(Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++i) + { + Xml::Node& arg = **ao; + file << signature_to_type(arg.get("type")) << "&"; + + string arg_name = arg.get("name"); + if(arg_name.length()) + file << " " << arg_name; + + if(i+1 != args_out.size()) + file << ", "; + } + } + file << " ) = 0;" << endl; + } + + file << endl + << "public:" << endl + << endl + << tab << "/* signal emitters for this interface" << endl + << tab << " */" << endl; + + for(Xml::Nodes::iterator si = signals.begin(); si != signals.end(); ++si) + { + Xml::Node& signal = **si; + Xml::Nodes args = signal["arg"]; + + file << tab << "void " << signal.get("name") << "( "; + + unsigned int i = 0; + for(Xml::Nodes::iterator a = args.begin(); a != args.end(); ++a, ++i) + { + Xml::Node& arg = **a; + + file << "const " << signature_to_type(arg.get("type")) << "& arg" << i+1; + + if(i+1 != args.size()) + file << ", "; + } + + file << " )" << endl + << tab << "{" << endl + << tab << tab << "::DBus::SignalMessage sig(\"" << signal.get("name") <<"\");" << endl;; + + + if(args.size() > 0) + { + file << tab << tab << "::DBus::MessageIter wi = sig.writer();" << endl; + + for(unsigned int i = 0; i < args.size(); ++i) + { + file << tab << tab << "wi << arg" << i+1 << ";" << endl; + } + } + + file << tab << tab << "emit_signal(sig);" << endl + << tab << "}" << endl; + } + + file << endl + << "private:" << endl + << endl + << tab << "/* unmarshalers (to unpack the DBus message before calling the actual interface method)" << endl + << tab << " */" << endl; + + for(Xml::Nodes::iterator mi = methods.begin(); mi != methods.end(); ++mi) + { + Xml::Node& method = **mi; + Xml::Nodes args = method["arg"]; + Xml::Nodes args_in = args.select("direction","in"); + Xml::Nodes args_out = args.select("direction","out"); + + file << tab << "::DBus::Message " << stub_name(method.get("name")) << "( const ::DBus::CallMessage& call )" << endl + << tab << "{" << endl + << tab << tab << "::DBus::MessageIter ri = call.reader();" << endl + << endl; + + unsigned int i = 1; + for(Xml::Nodes::iterator ai = args_in.begin(); ai != args_in.end(); ++ai, ++i) + { + Xml::Node& arg = **ai; + file << tab << tab << signature_to_type(arg.get("type")) << " argin" << i << ";" + << " ri >> argin" << i << ";" << endl; + } + + if(args_out.size() == 0) + { + file << tab << tab; + } + else if(args_out.size() == 1) + { + file << tab << tab << signature_to_type(args_out.front()->get("type")) << " argout1 = "; + } + else + { + unsigned int i = 1; + for(Xml::Nodes::iterator ao = args_out.begin(); ao != args_out.end(); ++ao, ++i) + { + Xml::Node& arg = **ao; + file << tab << tab << signature_to_type(arg.get("type")) << " argout" << i << ";" << endl; + } + file << tab << tab; + } + + file << method.get("name") << "("; + + for(unsigned int i = 0; i < args_in.size(); ++i) + { + file << "argin" << i+1; + + if((i+1 != args_in.size() || args_out.size() > 1)) + file << ", "; + } + + if(args_out.size() > 1) + for(unsigned int i = 0; i < args_out.size(); ++i) + { + file << "argout" << i+1; + + if(i+1 != args_out.size()) + file << ", "; + } + + file << ");" << endl; + + file << tab << tab << "::DBus::ReturnMessage reply(call);" << endl; + + if(args_out.size() > 0) + { + file << tab << tab << "::DBus::MessageIter wi = reply.writer();" << endl; + + for(unsigned int i = 0; i < args_out.size(); ++i) + { + file << tab << tab << "wi << argout" << i+1 << ";" << endl; + } + } + + file << tab << tab << "return reply;" << endl; + + file << tab << "}" << endl; + } + + file << "};" << endl + << endl; + + for(unsigned int i = 0; i < nspaces; ++i) + { + file << "} "; + } + file << endl; + } + + file << "#endif//" << cond_comp << endl; + + file.close(); +} + +int main( int argc, char** argv ) +{ + if(argc < 2) + { + usage(argv[0]); + } + + bool proxy_mode, adaptor_mode; + char *proxy, *adaptor; + + proxy_mode = false; + proxy = 0; + + adaptor_mode = false; + adaptor = 0; + + for(int a = 1; a < argc; ++a) + { + if(!strncmp(argv[a], "--proxy=", 8)) + { + proxy_mode = true; + proxy = argv[a] +8; + } + else + if(!strncmp(argv[a], "--adaptor=", 10)) + { + adaptor_mode = true; + adaptor = argv[a] +10; + } + } + + if(!proxy_mode && !adaptor_mode) usage(argv[0]); + + ifstream xmlfile(argv[1]); + + if(xmlfile.bad()) + { + cerr << "unable to open file " << argv[1] << endl; + return -1; + } + + Xml::Document doc; + + try + { + xmlfile >> doc; + //cout << doc.to_xml(); + } + catch(Xml::Error& e) + { + cerr << "error parsing " << argv[1] << ": " << e.what() << endl; + return -1; + } + + if(!doc.root) + { + cerr << "empty document" << endl; + return -1; + } + + if(proxy_mode) generate_proxy(doc, proxy); + if(adaptor_mode) generate_adaptor(doc, adaptor); + + return 0; +} Index: /branches/libffado-2.0/external/dbus/tools/introspect.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/tools/introspect.cpp (revision 562) +++ /branches/libffado-2.0/external/dbus/tools/introspect.cpp (revision 562) @@ -0,0 +1,78 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include "introspect.h" + +DBus::BusDispatcher dispatcher; +static bool systembus; +static char* path; +static char* service; + +void niam( int sig ) +{ + DBus::Connection conn = systembus ? DBus::Connection::SystemBus() : DBus::Connection::SessionBus(); + + IntrospectedObject io(conn, path, service); + + std::cout << io.Introspect(); + + dispatcher.leave(); +} + +int main( int argc, char** argv ) +{ + signal(SIGTERM, niam); + signal(SIGINT, niam); + signal(SIGALRM, niam); + + if(argc == 1) + { + std::cerr << std::endl << "Usage: " << argv[0] << " [--system] []" << std::endl << std::endl; + } + else + { + if(strcmp(argv[1], "--system")) + { + systembus = false; + path = argv[1]; + service = argc > 2 ? argv[2] : 0; + } + else + { + systembus = true; + path = argv[2]; + service = argc > 3 ? argv[3] : 0; + } + + DBus::default_dispatcher = &dispatcher; + + alarm(1); + + dispatcher.enter(); + } + + return 0; +} Index: /branches/libffado-2.0/external/dbus/tools/xml2cpp.h =================================================================== --- /branches/libffado-2.0/external/dbus/tools/xml2cpp.h (revision 562) +++ /branches/libffado-2.0/external/dbus/tools/xml2cpp.h (revision 562) @@ -0,0 +1,37 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_TOOLS_XML2CPP_H +#define __DBUSXX_TOOLS_XML2CPP_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "xml.h" + +#endif//__DBUSXX_TOOLS_XML2CPP_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/error.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/error.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/error.h (revision 562) @@ -0,0 +1,289 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_ERROR_H +#define __DBUSXX_ERROR_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "api.h" +#include "util.h" + +#include + +namespace DBus { + +class Message; +class InternalError; + +class DXXAPI Error : public std::exception +{ +public: + + Error(); + + Error( InternalError& ); + + Error( const char* name, const char* message ); + + Error( Message& ); + + ~Error() throw(); + + const char* what() const throw(); + + const char* name() const; + + const char* message() const; + + void set( const char* name, const char* message ); + // parameters MUST be static strings + + bool is_set() const; + + operator bool() const + { + return is_set(); + } + +private: + + RefPtrI _int; +}; + +struct DXXAPI ErrorFailed : public Error +{ + ErrorFailed( const char* message ) + : Error("org.freedesktop.DBus.Error.Failed", message) + {} +}; + +struct DXXAPI ErrorNoMemory : public Error +{ + ErrorNoMemory( const char* message ) + : Error("org.freedesktop.DBus.Error.NoMemory", message) + {} +}; + +struct DXXAPI ErrorServiceUnknown : public Error +{ + ErrorServiceUnknown( const char* message ) + : Error("org.freedesktop.DBus.Error.ServiceUnknown", message) + {} +}; + +struct DXXAPI ErrorNameHasNoOwner : public Error +{ + ErrorNameHasNoOwner( const char* message ) + : Error("org.freedesktop.DBus.Error.NameHasNoOwner", message) + {} +}; + +struct DXXAPI ErrorNoReply : public Error +{ + ErrorNoReply( const char* message ) + : Error("org.freedesktop.DBus.Error.NoReply", message) + {} +}; + +struct DXXAPI ErrorIOError : public Error +{ + ErrorIOError( const char* message ) + : Error("org.freedesktop.DBus.Error.IOError", message) + {} +}; + +struct DXXAPI ErrorBadAddress : public Error +{ + ErrorBadAddress( const char* message ) + : Error("org.freedesktop.DBus.Error.BadAddress", message) + {} +}; + +struct DXXAPI ErrorNotSupported : public Error +{ + ErrorNotSupported( const char* message ) + : Error("org.freedesktop.DBus.Error.NotSupported", message) + {} +}; + +struct DXXAPI ErrorLimitsExceeded : public Error +{ + ErrorLimitsExceeded( const char* message ) + : Error("org.freedesktop.DBus.Error.LimitsExceeded", message) + {} +}; + +struct DXXAPI ErrorAccessDenied : public Error +{ + ErrorAccessDenied( const char* message ) + : Error("org.freedesktop.DBus.Error.AccessDenied", message) + {} +}; + +struct DXXAPI ErrorAuthFailed : public Error +{ + ErrorAuthFailed( const char* message ) + : Error("org.freedesktop.DBus.Error.AuthFailed", message) + {} +}; + +struct DXXAPI ErrorNoServer : public Error +{ + ErrorNoServer( const char* message ) + : Error("org.freedesktop.DBus.Error.NoServer", message) + {} +}; + +struct DXXAPI ErrorTimeout : public Error +{ + ErrorTimeout( const char* message ) + : Error("org.freedesktop.DBus.Error.Timeout", message) + {} +}; + +struct DXXAPI ErrorNoNetwork : public Error +{ + ErrorNoNetwork( const char* message ) + : Error("org.freedesktop.DBus.Error.NoNetwork", message) + {} +}; + +struct DXXAPI ErrorAddressInUse : public Error +{ + ErrorAddressInUse( const char* message ) + : Error("org.freedesktop.DBus.Error.AddressInUse", message) + {} +}; + +struct DXXAPI ErrorDisconnected : public Error +{ + ErrorDisconnected( const char* message ) + : Error("org.freedesktop.DBus.Error.Disconnected", message) + {} +}; + +struct DXXAPI ErrorInvalidArgs : public Error +{ + ErrorInvalidArgs( const char* message ) + : Error("org.freedesktop.DBus.Error.InvalidArgs", message) + {} +}; + +struct DXXAPI ErrorFileNotFound : public Error +{ + ErrorFileNotFound( const char* message ) + : Error("org.freedesktop.DBus.Error.FileNotFound", message) + {} +}; + +struct DXXAPI ErrorUnknownMethod : public Error +{ + ErrorUnknownMethod( const char* message ) + : Error("org.freedesktop.DBus.Error.UnknownMethod", message) + {} +}; + +struct DXXAPI ErrorTimedOut : public Error +{ + ErrorTimedOut( const char* message ) + : Error("org.freedesktop.DBus.Error.TimedOut", message) + {} +}; + +struct DXXAPI ErrorMatchRuleNotFound : public Error +{ + ErrorMatchRuleNotFound( const char* message ) + : Error("org.freedesktop.DBus.Error.MatchRuleNotFound", message) + {} +}; + +struct DXXAPI ErrorMatchRuleInvalid : public Error +{ + ErrorMatchRuleInvalid( const char* message ) + : Error("org.freedesktop.DBus.Error.MatchRuleInvalid", message) + {} +}; + +struct DXXAPI ErrorSpawnExecFailed : public Error +{ + ErrorSpawnExecFailed( const char* message ) + : Error("org.freedesktop.DBus.Error.Spawn.ExecFailed", message) + {} +}; + +struct DXXAPI ErrorSpawnForkFailed : public Error +{ + ErrorSpawnForkFailed( const char* message ) + : Error("org.freedesktop.DBus.Error.Spawn.ForkFailed", message) + {} +}; + +struct DXXAPI ErrorSpawnChildExited : public Error +{ + ErrorSpawnChildExited( const char* message ) + : Error("org.freedesktop.DBus.Error.Spawn.ChildExited", message) + {} +}; + +struct DXXAPI ErrorSpawnChildSignaled : public Error +{ + ErrorSpawnChildSignaled( const char* message ) + : Error("org.freedesktop.DBus.Error.Spawn.ChildSignaled", message) + {} +}; + +struct DXXAPI ErrorSpawnFailed : public Error +{ + ErrorSpawnFailed( const char* message ) + : Error("org.freedesktop.DBus.Error.Spawn.Failed", message) + {} +}; + +struct DXXAPI ErrorInvalidSignature : public Error +{ + ErrorInvalidSignature( const char* message ) + : Error("org.freedesktop.DBus.Error.InvalidSignature", message) + {} +}; + +struct DXXAPI ErrorUnixProcessIdUnknown : public Error +{ + ErrorUnixProcessIdUnknown( const char* message ) + : Error("org.freedesktop.DBus.Error.UnixProcessIdUnknown", message) + {} +}; + +struct DXXAPI ErrorSELinuxSecurityContextUnknown : public Error +{ + ErrorSELinuxSecurityContextUnknown( const char* message ) + : Error("org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown", message) + {} +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_ERROR_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/dbus.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/dbus.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/dbus.h (revision 562) @@ -0,0 +1,48 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_DBUS_H +#define __DBUSXX_DBUS_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "types.h" +#include "interface.h" +#include "object.h" +#include "property.h" +#include "connection.h" +#include "server.h" +#include "error.h" +#include "message.h" +#include "debug.h" +#include "pendingcall.h" +#include "server.h" +#include "util.h" +#include "dispatcher.h" +#include "eventloop.h" +#include "introspection.h" + +#endif//__DBUSXX_DBUS_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/glib-integration.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/glib-integration.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/glib-integration.h (revision 562) @@ -0,0 +1,119 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_GLIB_INTEGRATION_H +#define __DBUSXX_GLIB_INTEGRATION_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "api.h" +#include "eventloop.h" + +namespace DBus { + +namespace Glib { + +class BusDispatcher; + +class DXXAPI BusTimeout : public Timeout +{ +private: + + BusTimeout( Timeout::Internal*, GMainContext* ); + + ~BusTimeout(); + + void toggle(); + + static gboolean timeout_handler( gpointer ); + + void _enable(); + + void _disable(); + +private: + + GSource* _source; + GMainContext* _ctx; + +friend class BusDispatcher; +}; + +class DXXAPI BusWatch : public Watch +{ +private: + + BusWatch( Watch::Internal*, GMainContext* ); + + ~BusWatch(); + + void toggle(); + + static gboolean watch_handler( gpointer ); + + void _enable(); + + void _disable(); + +private: + + GSource* _source; + GMainContext* _ctx; + +friend class BusDispatcher; +}; + +class DXXAPI BusDispatcher : public Dispatcher +{ +public: + BusDispatcher() : _ctx(NULL) {} + + void attach( GMainContext* ); + + void enter() {} + + void leave() {} + + Timeout* add_timeout( Timeout::Internal* ); + + void rem_timeout( Timeout* ); + + Watch* add_watch( Watch::Internal* ); + + void rem_watch( Watch* ); + +private: + + GMainContext* _ctx; +}; + +} /* namespace Glib */ + +} /* namespace DBus */ + +#endif//__DBUSXX_GLIB_INTEGRATION_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/connection.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/connection.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/connection.h (revision 562) @@ -0,0 +1,126 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_CONNECTION_H +#define __DBUSXX_CONNECTION_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "api.h" +#include "types.h" +#include "util.h" +#include "message.h" +#include "pendingcall.h" + +namespace DBus { + +class Connection; + +typedef Slot MessageSlot; + +typedef std::list ConnectionList; + +class ObjectAdaptor; +class Dispatcher; + +class DXXAPI Connection +{ +public: + + static Connection SystemBus(); + + static Connection SessionBus(); + + static Connection ActivationBus(); + + struct Private; + + typedef std::list PrivatePList; + + Connection( Private* ); + + Connection( const char* address, bool priv = true ); + + Connection( const Connection& c ); + + virtual ~Connection(); + + Dispatcher* setup( Dispatcher* ); + + bool operator == ( const Connection& ) const; + + void add_match( const char* rule ); + + void remove_match( const char* rule ); + + bool add_filter( MessageSlot& ); + + void remove_filter( MessageSlot& ); + + bool unique_name( const char* n ); + + const char* unique_name() const; + + bool register_bus(); + + bool connected() const; + + void disconnect(); + + void exit_on_disconnect( bool exit ); + + void flush(); + + bool send( const Message&, unsigned int* serial = NULL ); + + Message send_blocking( Message& msg, int timeout ); + + PendingCall send_async( Message& msg, int timeout ); + + void request_name( const char* name, int flags = 0 ); + + bool has_name( const char* name ); + + bool start_service( const char* name, unsigned long flags ); + + const std::vector& names(); + +private: + + DXXAPILOCAL void init(); + +private: + + RefPtrI _pvt; + +friend class ObjectAdaptor; // needed in order to register object paths for a connection +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_CONNECTION_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/introspection.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/introspection.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/introspection.h (revision 562) @@ -0,0 +1,90 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_INTROSPECTION_H +#define __DBUSXX_INTROSPECTION_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "api.h" +#include "interface.h" + +namespace DBus { + +struct DXXAPI IntrospectedArgument +{ + const char* name; + const char* type; + const bool in; +}; + +struct DXXAPI IntrospectedMethod +{ + const char* name; + const IntrospectedArgument* args; +}; + +struct DXXAPI IntrospectedProperty +{ + const char* name; + const char* type; + const bool read; + const bool write; +}; + +struct DXXAPI IntrospectedInterface +{ + const char* name; + const IntrospectedMethod* methods; + const IntrospectedMethod* signals; + const IntrospectedProperty* properties; +}; + +class DXXAPI IntrospectableAdaptor : public InterfaceAdaptor +{ +public: + + IntrospectableAdaptor(); + + Message Introspect( const CallMessage& ); + +protected: + + IntrospectedInterface* const introspect() const; +}; + +class DXXAPI IntrospectableProxy : public InterfaceProxy +{ +public: + + IntrospectableProxy(); + + std::string Introspect(); +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_INTROSPECTION_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/interface.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/interface.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/interface.h (revision 562) @@ -0,0 +1,195 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_INTERFACE_H +#define __DBUSXX_INTERFACE_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "api.h" +#include "util.h" +#include "types.h" + +#include "message.h" + +namespace DBus { + +//todo: this should belong to to properties.h +struct DXXAPI PropertyData +{ + bool read; + bool write; + std::string sig; + Variant value; +}; + +typedef std::map PropertyTable; + +class IntrospectedInterface; + +class ObjectAdaptor; +class InterfaceAdaptor; +class SignalMessage; + +typedef std::map InterfaceAdaptorTable; + +class DXXAPI AdaptorBase +{ +public: + + virtual const ObjectAdaptor* object() const = 0 ; + +protected: + + InterfaceAdaptor* find_interface( const std::string& name ); + + virtual ~AdaptorBase() + {} + + virtual void _emit_signal( SignalMessage& ) = 0; + + InterfaceAdaptorTable _interfaces; +}; + +/* +*/ + +class ObjectProxy; +class InterfaceProxy; +class CallMessage; + +typedef std::map InterfaceProxyTable; + +class DXXAPI ProxyBase +{ +public: + + virtual const ObjectProxy* object() const = 0 ; + +protected: + + InterfaceProxy* find_interface( const std::string& name ); + + virtual ~ProxyBase() + {} + + virtual Message _invoke_method( CallMessage& ) = 0; + + InterfaceProxyTable _interfaces; +}; + +class DXXAPI Interface +{ +public: + + Interface( const std::string& name ); + + virtual ~Interface(); + + inline const std::string& name() const; + +private: + + std::string _name; +}; + +/* +*/ + +const std::string& Interface::name() const +{ + return _name; +} + +/* +*/ + +typedef std::map< std::string, Slot > MethodTable; + +class DXXAPI InterfaceAdaptor : public Interface, public virtual AdaptorBase +{ +public: + + InterfaceAdaptor( const std::string& name ); + + Message dispatch_method( const CallMessage& ); + + void emit_signal( const SignalMessage& ); + + Variant* get_property( const std::string& name ); + + void set_property( const std::string& name, Variant& value ); + + virtual IntrospectedInterface* const introspect() const + { + return NULL; + } + +protected: + + MethodTable _methods; + PropertyTable _properties; +}; + +/* +*/ + +typedef std::map< std::string, Slot > SignalTable; + +class DXXAPI InterfaceProxy : public Interface, public virtual ProxyBase +{ +public: + + InterfaceProxy( const std::string& name ); + + Message invoke_method( const CallMessage& ); + + bool dispatch_signal( const SignalMessage& ); + +protected: + + SignalTable _signals; +}; + +# define register_method(interface, method, callback) \ + InterfaceAdaptor::_methods[ #method ] = \ + new ::DBus::Callback< interface, ::DBus::Message, const ::DBus::CallMessage& >(this, & interface :: callback ); + +# define bind_property(variable, type, can_read, can_write) \ + InterfaceAdaptor::_properties[ #variable ].read = can_read; \ + InterfaceAdaptor::_properties[ #variable ].write = can_write; \ + InterfaceAdaptor::_properties[ #variable ].sig = type; \ + variable.bind( InterfaceAdaptor::_properties[ #variable ] ); + +# define connect_signal(interface, signal, callback) \ + InterfaceProxy::_signals[ #signal ] = \ + new ::DBus::Callback< interface, void, const ::DBus::SignalMessage& >(this, & interface :: callback ); + +} /* namespace DBus */ + +#endif//__DBUSXX_INTERFACE_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/types.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/types.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/types.h (revision 562) @@ -0,0 +1,514 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_TYPES_H +#define __DBUSXX_TYPES_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "api.h" +#include "util.h" +#include "message.h" +#include "error.h" + +namespace DBus { + +typedef unsigned char Byte; +typedef bool Bool; +typedef signed short Int16; +typedef unsigned short UInt16; +typedef signed int Int32; +typedef unsigned int UInt32; +typedef signed long long Int64; +typedef unsigned long long UInt64; +typedef double Double; +typedef std::string String; + +struct DXXAPI Path : public std::string +{ + Path() {} + Path( const std::string& s ) : std::string(s) {} + Path( const char* c ) : std::string(c) {} + Path& operator = ( std::string& s ) + { + std::string::operator = (s); + return *this; + } +}; + +struct DXXAPI Signature : public std::string +{ + Signature() {} + Signature( const std::string& s ) : std::string(s) {} + Signature( const char* c ) : std::string(c) {} + Signature& operator = ( std::string& s ) + { + std::string::operator = (s); + return *this; + } +}; + +struct DXXAPI Invalid {}; + +class DXXAPI Variant +{ +public: + + Variant(); + + Variant( MessageIter& it ); + + Variant& operator = ( const Variant& v ); + + const Signature signature() const; + + void clear(); + + MessageIter reader() const + { + return _msg.reader(); + } + + MessageIter writer() + { + return _msg.writer(); + } + + template + operator T() const + { + T cast; + MessageIter ri = _msg.reader(); + ri >> cast; + return cast; + } + +private: + + Message _msg; +}; + +template < + typename T1, + typename T2 = Invalid, + typename T3 = Invalid, + typename T4 = Invalid, + typename T5 = Invalid, + typename T6 = Invalid, + typename T7 = Invalid, + typename T8 = Invalid // who needs more than eight? +> +struct Struct +{ + T1 _1; T2 _2; T3 _3; T4 _4; T5 _5; T6 _6; T7 _7; T8 _8; +}; + +template +inline bool dict_has_key( const std::map& map, const K& key ) +{ + return map.find(key) != map.end(); +} + +template +struct type +{ + static std::string sig() + { + throw ErrorInvalidArgs("unknown type"); + return ""; + } +}; + +template <> struct type { static std::string sig(){ return "v"; } }; +template <> struct type { static std::string sig(){ return "y"; } }; +template <> struct type { static std::string sig(){ return "b"; } }; +template <> struct type { static std::string sig(){ return "n"; } }; +template <> struct type { static std::string sig(){ return "q"; } }; +template <> struct type { static std::string sig(){ return "i"; } }; +template <> struct type { static std::string sig(){ return "u"; } }; +template <> struct type { static std::string sig(){ return "x"; } }; +template <> struct type { static std::string sig(){ return "t"; } }; +template <> struct type { static std::string sig(){ return "d"; } }; +template <> struct type { static std::string sig(){ return "s"; } }; +template <> struct type { static std::string sig(){ return "o"; } }; +template <> struct type { static std::string sig(){ return "g"; } }; +template <> struct type { static std::string sig(){ return ""; } }; + +template +struct type< std::vector > +{ static std::string sig(){ return "a" + type::sig(); } }; + +template +struct type< std::map > +{ static std::string sig(){ return "a{" + type::sig() + type::sig() + "}"; } }; + +template < + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8 // who needs more than eight? +> +struct type< Struct > +{ + static std::string sig() + { + return "(" + + type::sig() + + type::sig() + + type::sig() + + type::sig() + + type::sig() + + type::sig() + + type::sig() + + type::sig() + + ")"; + } +}; + +} /* namespace DBus */ + +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const DBus::Invalid& ) +{ + return iter; +} + +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const DBus::Byte& val ) +{ + iter.append_byte(val); + return iter; +} + +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const DBus::Bool& val ) +{ + iter.append_bool(val); + return iter; +} + +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const DBus::Int16& val ) +{ + iter.append_int16(val); + return iter; +} + +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const DBus::UInt16& val ) +{ + iter.append_uint16(val); + return iter; +} + +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const DBus::Int32& val ) +{ + iter.append_int32(val); + return iter; +} + +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const DBus::UInt32& val ) +{ + iter.append_uint32(val); + return iter; +} + +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const DBus::Int64& val ) +{ + iter.append_int64(val); + return iter; +} + +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const DBus::UInt64& val ) +{ + iter.append_uint64(val); + return iter; +} + +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const DBus::Double& val ) +{ + iter.append_double(val); + return iter; +} + +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const DBus::String& val ) +{ + iter.append_string(val.c_str()); + return iter; +} + +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const DBus::Path& val ) +{ + iter.append_path(val.c_str()); + return iter; +} + +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const DBus::Signature& val ) +{ + iter.append_signature(val.c_str()); + return iter; +} + +template +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const std::vector& val ) +{ + const std::string sig = DBus::type::sig(); + DBus::MessageIter ait = iter.new_array(sig.c_str()); + + typename std::vector::const_iterator vit; + for(vit = val.begin(); vit != val.end(); ++vit) + { + ait << *vit; + } + + iter.close_container(ait); + return iter; +} + +template<> +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const std::vector& val ) +{ + DBus::MessageIter ait = iter.new_array("y"); + ait.append_array('y', &val.front(), val.size()); + iter.close_container(ait); + return iter; +} + +template +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const std::map& val ) +{ + const std::string sig = "{" + DBus::type::sig() + DBus::type::sig() + "}"; + DBus::MessageIter ait = iter.new_array(sig.c_str()); + + typename std::map::const_iterator mit; + for(mit = val.begin(); mit != val.end(); ++mit) + { + DBus::MessageIter eit = ait.new_dict_entry(); + + eit << mit->first << mit->second; + + ait.close_container(eit); + } + + iter.close_container(ait); + return iter; +} + +template < + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8 +> +inline DBus::MessageIter& operator << ( DBus::MessageIter& iter, const DBus::Struct& val ) +{ +/* const std::string sig = + DBus::type::sig() + DBus::type::sig() + DBus::type::sig() + DBus::type::sig() + + DBus::type::sig() + DBus::type::sig() + DBus::type::sig() + DBus::type::sig(); +*/ + DBus::MessageIter sit = iter.new_struct(/*sig.c_str()*/); + + sit << val._1 << val._2 << val._3 << val._4 << val._5 << val._6 << val._7 << val._8; + + iter.close_container(sit); + + return iter; +} + +extern DXXAPI DBus::MessageIter& operator << ( DBus::MessageIter& iter, const DBus::Variant& val ); + +/* + */ + +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, DBus::Invalid& ) +{ + return iter; +} + +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, DBus::Byte& val ) +{ + val = iter.get_byte(); + return ++iter; +} + +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, DBus::Bool& val ) +{ + val = iter.get_bool(); + return ++iter; +} + +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, DBus::Int16& val ) +{ + val = iter.get_int16(); + return ++iter; +} + +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, DBus::UInt16& val ) +{ + val = iter.get_uint16(); + return ++iter; +} + +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, DBus::Int32& val ) +{ + val = iter.get_int32(); + return ++iter; +} + +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, DBus::UInt32& val ) +{ + val = iter.get_uint32(); + return ++iter; +} + +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, DBus::Int64& val ) +{ + val = iter.get_int64(); + return ++iter; +} + +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, DBus::UInt64& val ) +{ + val = iter.get_uint64(); + return ++iter; +} + +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, DBus::Double& val ) +{ + val = iter.get_double(); + return ++iter; +} + +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, DBus::String& val ) +{ + val = iter.get_string(); + return ++iter; +} + +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, DBus::Path& val ) +{ + val = iter.get_path(); + return ++iter; +} + +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, DBus::Signature& val ) +{ + val = iter.get_signature(); + return ++iter; +} + +template +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, std::vector& val ) +{ + if(!iter.is_array()) + throw DBus::ErrorInvalidArgs("array expected"); + + DBus::MessageIter ait = iter.recurse(); + + while(!ait.at_end()) + { + E elem; + + ait >> elem; + + val.push_back(elem); + } + return ++iter; +} + +template<> +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, std::vector& val ) +{ + if(!iter.is_array()) + throw DBus::ErrorInvalidArgs("array expected"); + + if(iter.array_type() != 'y') + throw DBus::ErrorInvalidArgs("byte-array expected"); + + DBus::MessageIter ait = iter.recurse(); + + DBus::Byte* array; + size_t length = ait.get_array(&array); + + val.insert(val.end(), array, array+length); + + return ++iter; +} + +template +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, std::map& val ) +{ + if(!iter.is_dict()) + throw DBus::ErrorInvalidArgs("dictionary value expected"); + + DBus::MessageIter mit = iter.recurse(); + + while(!mit.at_end()) + { + K key; V value; + + DBus::MessageIter eit = mit.recurse(); + + eit >> key >> value; + + val[key] = value; + + ++mit; + } + + return ++iter; +} + +template < + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8 +> +inline DBus::MessageIter& operator >> ( DBus::MessageIter& iter, DBus::Struct& val) +{ + DBus::MessageIter sit = iter.recurse(); + + sit >> val._1 >> val._2 >> val._3 >> val._4 >> val._5 >> val._6 >> val._7 >> val._8; + + return ++iter; +} + +extern DXXAPI DBus::MessageIter& operator >> ( DBus::MessageIter& iter, DBus::Variant& val ); + +#endif//__DBUSXX_TYPES_H + Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/config.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/config.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/config.h (revision 562) @@ -0,0 +1,74 @@ +/* include/dbus-c++/config.h. Generated from config.h.in by configure. */ +/* include/dbus-c++/config.h.in. Generated from configure.ac by autoheader. */ + +/* unstable DBus */ +/* #undef DBUS_API_SUBJECT_TO_CHANGE */ + +/* DBus supports recursive mutexes (needs DBus >= 0.95) */ +#define DBUS_HAS_RECURSIVE_MUTEX + +/* dbus_threads_init_default (needs DBus >= 0.93) */ +#define DBUS_HAS_THREADS_INIT_DEFAULT + +/* to enable hidden symbols */ +#define GCC_HASCLASSVISIBILITY 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_EXPAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_PTHREAD_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Name of package */ +#define PACKAGE "libdbus-c++" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "shackan@gmail.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libdbus-c++" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libdbus-c++ 0.5.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libdbus-c--" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.5.0" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "0.5.0" Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/object.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/object.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/object.h (revision 562) @@ -0,0 +1,223 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_OBJECT_H +#define __DBUSXX_OBJECT_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "api.h" +#include "interface.h" +#include "connection.h" +#include "message.h" +#include "types.h" + +namespace DBus { + +class DXXAPI Object +{ +protected: + + Object( Connection& conn, const Path& path, const char* service ); + +public: + + virtual ~Object(); + + inline const DBus::Path& path() const; + + inline const std::string& service() const; + + inline Connection& conn(); + +private: + + DXXAPILOCAL virtual bool handle_message( const Message& ) = 0; + DXXAPILOCAL virtual void register_obj() = 0; + DXXAPILOCAL virtual void unregister_obj() = 0; + +private: + + Connection _conn; + DBus::Path _path; + std::string _service; +}; + +/* +*/ + +Connection& Object::conn() +{ + return _conn; +} + +const DBus::Path& Object::path() const +{ + return _path; +} + +const std::string& Object::service() const +{ + return _service; +} + +/* +*/ + +class DXXAPI Tag +{ +public: + + virtual ~Tag() + {} +}; + +/* +*/ + +class ObjectAdaptor; + +typedef std::list ObjectAdaptorPList; + +class DXXAPI ObjectAdaptor : public Object, public virtual AdaptorBase +{ +public: + + static ObjectAdaptor* from_path( const Path& path ); + + static ObjectAdaptorPList from_path_prefix( const std::string& prefix ); + + struct Private; + + ObjectAdaptor( Connection& conn, const Path& path ); + + ~ObjectAdaptor(); + + inline const ObjectAdaptor* object() const; + +protected: + + class DXXAPI Continuation + { + public: + + inline MessageIter& writer(); + + inline Tag* tag(); + + private: + + Continuation( Connection& conn, const CallMessage& call, const Tag* tag ); + + Connection _conn; + CallMessage _call; + MessageIter _writer; + ReturnMessage _return; + const Tag* _tag; + + friend class ObjectAdaptor; + }; + + void return_later( const Tag* tag ); + + void return_now( Continuation* ret ); + + void return_error( Continuation* ret, const Error error ); + + Continuation* find_continuation( const Tag* tag ); + +private: + + void _emit_signal( SignalMessage& ); + + bool handle_message( const Message& ); + + void register_obj(); + void unregister_obj(); + + typedef std::map ContinuationMap; + ContinuationMap _continuations; + +friend struct Private; +}; + +const ObjectAdaptor* ObjectAdaptor::object() const +{ + return this; +} + +Tag* ObjectAdaptor::Continuation::tag() +{ + return const_cast(_tag); +} + +MessageIter& ObjectAdaptor::Continuation::writer() +{ + return _writer; +} + +/* +*/ + +class ObjectProxy; + +typedef std::list ObjectProxyPList; + +class DXXAPI ObjectProxy : public Object, public virtual ProxyBase +{ +public: + + ObjectProxy( Connection& conn, const Path& path, const char* service = "" ); + + ~ObjectProxy(); + + inline const ObjectProxy* object() const; + +private: + + Message _invoke_method( CallMessage& ); + + bool handle_message( const Message& ); + + void register_obj(); + void unregister_obj(); + +private: + + MessageSlot _filtered; +}; + +const ObjectProxy* ObjectProxy::object() const +{ + return this; +} + +} /* namespace DBus */ + +#endif//__DBUSXX_OBJECT_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/server.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/server.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/server.h (revision 562) @@ -0,0 +1,78 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_SERVER_H +#define __DBUSXX_SERVER_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "api.h" +#include "error.h" +#include "connection.h" +#include "util.h" +#include "dispatcher.h" + +namespace DBus { + +class Server; + +typedef std::list ServerList; + +class DXXAPI Server +{ +public: + + Server( const char* address ); + + Dispatcher* setup( Dispatcher* ); + + virtual ~Server(); + + bool listening() const; + + bool operator == ( const Server& ) const; + + void disconnect(); + + struct Private; + +protected: + + Server( const Server& s ) + {} + + virtual void on_new_connection( Connection& c ) = 0; + +private: + + RefPtrI _pvt; +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_SERVER_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/api.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/api.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/api.h (revision 562) @@ -0,0 +1,42 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_API_H +#define __DBUSXX_API_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef GCC_HASCLASSVISIBILITY +# define DXXAPILOCAL __attribute__ ((visibility("hidden"))) +# define DXXAPIPUBLIC __attribute__ ((visibility("default"))) +#else +# define DXXAPILOCAL +# define DXXAPIPUBLIC +#endif + +#define DXXAPI DXXAPIPUBLIC + +#endif//__DBUSXX_API_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/eventloop.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/eventloop.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/eventloop.h (revision 562) @@ -0,0 +1,202 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_EVENTLOOP_H +#define __DBUSXX_EVENTLOOP_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "api.h" +#include "dispatcher.h" +#include "util.h" + +namespace DBus { + +class EepleMainLoop; + +class DXXAPI EepleTimeout +{ +public: + + EepleTimeout( int interval, bool repeat, EepleMainLoop* ); + + virtual ~EepleTimeout(); + + bool enabled(){ return _enabled; } + void enabled(bool e){ _enabled = e; } + + int interval(){ return _interval; } + void interval(int i){ _interval = i; } + + bool repeat(){ return _repeat; } + void repeat(bool r){ _repeat = r; } + + void* data(){ return _data; } + void data(void* d){ _data = d; } + + Slot expired; + +private: + + bool _enabled; + + int _interval; + bool _repeat; + + double _expiration; + + void* _data; + + EepleMainLoop* _disp; + +friend class EepleMainLoop; +}; + +typedef std::list< EepleTimeout* > Timeouts; + +class DXXAPI EepleWatch +{ +public: + + EepleWatch( int fd, int flags, EepleMainLoop* ); + + virtual ~EepleWatch(); + + bool enabled(){ return _enabled; } + void enabled(bool e){ _enabled = e; } + + int descriptor(){ return _fd; } + + int flags(){ return _flags; } + void flags( int f ){ _flags = f; } + + int state(){ return _state; } + + void* data(){ return _data; } + void data(void* d){ _data = d; } + + Slot ready; + +private: + + bool _enabled; + + int _fd; + int _flags; + int _state; + + void* _data; + + EepleMainLoop* _disp; + +friend class EepleMainLoop; +}; + +typedef std::list< EepleWatch* > Watches; + +class DXXAPI EepleMainLoop +{ +public: + + EepleMainLoop(); + + virtual ~EepleMainLoop(); + + virtual void dispatch(); + +private: + + Timeouts _timeouts; + Watches _watches; + +friend class EepleTimeout; +friend class EepleWatch; +}; + +/* the classes below are those you are going to implement if you + * want to use another event loop (Qt, Glib, boost, whatever). + * + * Don't forget to set 'default_dispatcher' accordingly! + */ + +class BusDispatcher; + +class DXXAPI BusTimeout : public Timeout, public EepleTimeout +{ + BusTimeout( Timeout::Internal*, BusDispatcher* ); + + void toggle(); + +friend class BusDispatcher; +}; + +class DXXAPI BusWatch : public Watch, public EepleWatch +{ + BusWatch( Watch::Internal*, BusDispatcher* ); + + void toggle(); + +friend class BusDispatcher; +}; + +class DXXAPI BusDispatcher : public Dispatcher, public EepleMainLoop +{ +public: + + BusDispatcher() : _running(false) + {} + + ~BusDispatcher() + {} + + virtual void enter(); + + virtual void leave(); + + virtual void do_iteration(); + + virtual Timeout* add_timeout( Timeout::Internal* ); + + virtual void rem_timeout( Timeout* ); + + virtual Watch* add_watch( Watch::Internal* ); + + virtual void rem_watch( Watch* ); + + void watch_ready( EepleWatch& ); + + void timeout_expired( EepleTimeout& ); + +private: + + bool _running; +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_EVENTLOOP_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/util.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/util.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/util.h (revision 562) @@ -0,0 +1,277 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_UTIL_H +#define __DBUSXX_UTIL_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "api.h" +#include "debug.h" + +namespace DBus { + +/* + * Very simple reference counting + */ + +class DXXAPI RefCnt +{ +public: + + RefCnt() + { + __ref = new int; + (*__ref) = 1; + } + + RefCnt( const RefCnt& rc ) + { + __ref = rc.__ref; + ref(); + } + + virtual ~RefCnt() + { + unref(); + } + + RefCnt& operator = ( const RefCnt& ref ) + { + ref.ref(); + unref(); + __ref = ref.__ref; + return *this; + } + + bool noref() const + { + return (*__ref) == 0; + } + + bool one() const + { + return (*__ref) == 1; + } + +private: + + DXXAPILOCAL void ref() const + { + ++ (*__ref); + } + DXXAPILOCAL void unref() const + { + -- (*__ref); + + if( (*__ref) < 0 ) + { + debug_log("%p: refcount dropped below zero!", __ref); + } + + if( noref() ) + { + delete __ref; + } + } + +private: + + int *__ref; +}; + +/* + * Reference counting pointers (emulate boost::shared_ptr) + */ + +template +class RefPtrI // RefPtr to incomplete type +{ +public: + + RefPtrI( T* ptr = 0 ); + + ~RefPtrI(); + + RefPtrI& operator = ( const RefPtrI& ref ) + { + if( this != &ref ) + { + if(__cnt.one()) delete __ptr; + + __ptr = ref.__ptr; + __cnt = ref.__cnt; + } + return *this; + } + + T& operator *() const + { + return *__ptr; + } + + T* operator ->() const + { + if(__cnt.noref()) return 0; + + return __ptr; + } + + T* get() const + { + if(__cnt.noref()) return 0; + + return __ptr; + } + +private: + + T* __ptr; + RefCnt __cnt; +}; + +template +class RefPtr +{ +public: + + RefPtr( T* ptr = 0) + : __ptr(ptr) + {} + + ~RefPtr() + { + if(__cnt.one()) delete __ptr; + } + + RefPtr& operator = ( const RefPtr& ref ) + { + if( this != &ref ) + { + if(__cnt.one()) delete __ptr; + + __ptr = ref.__ptr; + __cnt = ref.__cnt; + } + return *this; + } + + T& operator *() const + { + return *__ptr; + } + + T* operator ->() const + { + if(__cnt.noref()) return 0; + + return __ptr; + } + + T* get() const + { + if(__cnt.noref()) return 0; + + return __ptr; + } + +private: + + T* __ptr; + RefCnt __cnt; +}; + +/* + * Typed callback template + */ + +template +class Callback_Base +{ +public: + + virtual R call( P param ) const = 0; + + virtual ~Callback_Base() + {} +}; + +template +class Slot +{ +public: + + Slot& operator = ( Callback_Base* s ) + { + _cb = s; + + return *this; + } + + R operator()( P param ) const + { + /*if(_cb.get())*/ return _cb->call(param); + } + + R call( P param ) const + { + /*if(_cb.get())*/ return _cb->call(param); + } + + bool empty() + { + return _cb.get() == 0; + } + +private: + + RefPtr< Callback_Base > _cb; +}; + +template +class Callback : public Callback_Base +{ +public: + + typedef R (C::*M)(P); + + Callback( C* c, M m ) + : _c(c), _m(m) + {} + + R call( P param ) const + { + /*if(_c)*/ return (_c->*_m)(param); + } + +private: + + C* _c; M _m; +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_UTIL_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/pendingcall.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/pendingcall.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/pendingcall.h (revision 562) @@ -0,0 +1,75 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_PENDING_CALL_H +#define __DBUSXX_PENDING_CALL_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "api.h" +#include "util.h" + +namespace DBus { + +class Connection; + +class DXXAPI PendingCall +{ +public: + + struct Private; + + PendingCall( Private* ); + + virtual ~PendingCall(); + + bool completed(); + + void cancel(); + + void block(); + + void data( void* ); + + void* data(); + + Slot slot; + +private: + + DXXAPILOCAL PendingCall( const PendingCall& ); + +private: + + RefPtrI _pvt; + +friend struct Private; +friend class Connection; +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_PENDING_CALL_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/refptr_impl.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/refptr_impl.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/refptr_impl.h (revision 562) @@ -0,0 +1,50 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_REFPTR_IMPL_H +#define __DBUSXX_REFPTR_IMPL_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "api.h" +#include "util.h" + +namespace DBus { + +template +RefPtrI::RefPtrI( T* ptr ) +: __ptr(ptr) +{} + +template +RefPtrI::~RefPtrI() +{ + if(__cnt.one()) delete __ptr; +} + +} /* namespace DBus */ + +#endif//__DBUSXX_REFPTR_IMPL_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/property.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/property.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/property.h (revision 562) @@ -0,0 +1,106 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_PROPERTY_H +#define __DBUSXX_PROPERTY_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "api.h" +#include "types.h" +#include "interface.h" + +namespace DBus { + +template +class PropertyAdaptor +{ +public: + + PropertyAdaptor() : _data(0) + {} + + void bind( PropertyData& data ) + { + _data = &data; + } + + T operator() (void) const + { + return (T)_data->value; + } + + PropertyAdaptor& operator = ( const T& t ) + { + _data->value.clear(); + MessageIter wi = _data->value.writer(); + wi << t; + return *this; + } + +private: + + PropertyData* _data; +}; + +struct IntrospectedInterface; + +class DXXAPI PropertiesAdaptor : public InterfaceAdaptor +{ +public: + + PropertiesAdaptor(); + + Message Get( const CallMessage& ); + + Message Set( const CallMessage& ); + +protected: + + virtual void on_get_property( InterfaceAdaptor& interface, const String& property, Variant& value ) + {} + + virtual void on_set_property( InterfaceAdaptor& interface, const String& property, const Variant& value ) + {} + + IntrospectedInterface* const introspect() const; +}; + +class DXXAPI PropertiesProxy : public InterfaceProxy +{ +public: + + PropertiesProxy(); + + Variant Get( const String& interface, const String& property ); + + void Set( const String& interface, const String& property, const Variant& value ); +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_PROPERTY_H + Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/message.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/message.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/message.h (revision 562) @@ -0,0 +1,312 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_MESSAGE_H +#define __DBUSXX_MESSAGE_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "api.h" +#include "util.h" + +namespace DBus { + +class Message; +class ErrorMessage; +class SignalMessage; +class ReturnMessage; +class Error; +class Connection; + +class DXXAPI MessageIter +{ +public: + + MessageIter() {} + + int type(); + + bool at_end(); + + bool has_next(); + + MessageIter& operator ++(); + + MessageIter operator ++(int); + + bool append_byte( unsigned char byte ); + + unsigned char get_byte(); + + bool append_bool( bool b ); + + bool get_bool(); + + bool append_int16( signed short i ); + + signed short get_int16(); + + bool append_uint16( unsigned short u ); + + unsigned short get_uint16(); + + bool append_int32( signed int i ); + + signed int get_int32(); + + bool append_uint32( unsigned int u ); + + unsigned int get_uint32(); + + bool append_int64( signed long long i ); + + signed long long get_int64(); + + bool append_uint64( unsigned long long i ); + + unsigned long long get_uint64(); + + bool append_double( double d ); + + double get_double(); + + bool append_string( const char* chars ); + + const char* get_string(); + + bool append_path( const char* chars ); + + const char* get_path(); + + bool append_signature( const char* chars ); + + const char* get_signature(); + + char* signature() const; //returned string must be manually free()'d + + MessageIter recurse(); + + bool append_array( char type, const void* ptr, size_t length ); + + int array_type(); + + int get_array( void* ptr ); + + bool is_array(); + + bool is_dict(); + + MessageIter new_array( const char* sig ); + + MessageIter new_variant( const char* sig ); + + MessageIter new_struct(); + + MessageIter new_dict_entry(); + + void close_container( MessageIter& container ); + + void copy_data( MessageIter& to ); + + Message& msg() const + { + return *_msg; + } + +private: + + DXXAPILOCAL MessageIter(Message& msg) : _msg(&msg) {} + + DXXAPILOCAL bool append_basic( int type_id, void* value ); + + DXXAPILOCAL void get_basic( int type_id, void* ptr ); + +private: + + /* I'm sorry, but don't want to include dbus.h in the public api + */ + unsigned char _iter[sizeof(void*)*3+sizeof(int)*11]; + + Message* _msg; + +friend class Message; +}; + +class DXXAPI Message +{ +public: + + struct Private; + + Message( Private*, bool incref = true ); + + Message( const Message& m ); + + ~Message(); + + Message& operator = ( const Message& m ); + + Message copy(); + + int type() const; + + int serial() const; + + int reply_serial() const; + + bool reply_serial( int ); + + const char* sender() const; + + bool sender( const char* s ); + + const char* destination() const; + + bool destination( const char* s ); + + bool is_error() const; + + bool is_signal( const char* interface, const char* member ) const; + + MessageIter reader() const; + + MessageIter writer(); + + bool append( int first_type, ... ); + + void terminate(); + +protected: + + Message(); + +protected: + + RefPtrI _pvt; + +/* classes who need to read `_pvt` directly +*/ +friend class ErrorMessage; +friend class ReturnMessage; +friend class MessageIter; +friend class Error; +friend class Connection; +}; + +/* +*/ + +class DXXAPI ErrorMessage : public Message +{ +public: + + ErrorMessage(); + + ErrorMessage( const Message&, const char* name, const char* message ); + + const char* name() const; + + bool name( const char* n ); + + bool operator == ( const ErrorMessage& ) const; +}; + +/* +*/ + +class DXXAPI SignalMessage : public Message +{ +public: + + SignalMessage( const char* name ); + + SignalMessage( const char* path, const char* interface, const char* name ); + + const char* interface() const; + + bool interface( const char* i ); + + const char* member() const; + + bool member( const char* m ); + + const char* path() const; + + char** path_split() const; + + bool path( const char* p ); + + bool operator == ( const SignalMessage& ) const; +}; + +/* +*/ + +class DXXAPI CallMessage : public Message +{ +public: + + CallMessage(); + + CallMessage( const char* dest, const char* path, const char* iface, const char* method ); + + const char* interface() const; + + bool interface( const char* i ); + + const char* member() const; + + bool member( const char* m ); + + const char* path() const; + + char** path_split() const; + + bool path( const char* p ); + + const char* signature() const; + + bool operator == ( const CallMessage& ) const; +}; + +/* +*/ + +class DXXAPI ReturnMessage : public Message +{ +public: + + ReturnMessage( const CallMessage& callee ); + + const char* signature() const; +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_MESSAGE_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/dispatcher.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/dispatcher.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/dispatcher.h (revision 562) @@ -0,0 +1,258 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_DISPATCHER_H +#define __DBUSXX_DISPATCHER_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "api.h" +#include "connection.h" + +namespace DBus { + +class DXXAPI Timeout +{ +public: + + class Internal; + + Timeout( Internal* i ); + + virtual ~Timeout(){} + + int interval() const; + + bool enabled() const; + + bool handle(); + + virtual void toggle() = 0; + +private: + + DXXAPILOCAL Timeout( const Timeout& ); + +private: + + Internal* _int; +}; + +class DXXAPI Watch +{ +public: + + class Internal; + + Watch( Internal* i ); + + virtual ~Watch(){} + + int descriptor() const; + + int flags() const; + + bool enabled() const; + + bool handle( int flags ); + + virtual void toggle() = 0; + +private: + + DXXAPILOCAL Watch( const Watch& ); + +private: + + Internal* _int; +}; + +class DXXAPI Dispatcher +{ +public: + + virtual ~Dispatcher() + {} + + void queue_connection( Connection::Private* ); + + void dispatch_pending(); + + virtual void enter() = 0; + + virtual void leave() = 0; + + virtual Timeout* add_timeout( Timeout::Internal* ) = 0; + + virtual void rem_timeout( Timeout* ) = 0; + + virtual Watch* add_watch( Watch::Internal* ) = 0; + + virtual void rem_watch( Watch* ) = 0; + + struct Private; + +private: + + Connection::PrivatePList _pending_queue; +}; + +extern DXXAPI Dispatcher* default_dispatcher; + +/* classes for multithreading support +*/ + +class DXXAPI Mutex +{ +public: + + virtual ~Mutex() {} + + virtual void lock() = 0; + + virtual void unlock() = 0; + + struct Internal; + +protected: + + Internal* _int; +}; + +class DXXAPI CondVar +{ +public: + + virtual ~CondVar() {} + + virtual void wait( Mutex* ) = 0; + + virtual bool wait_timeout( Mutex*, int timeout ) = 0; + + virtual void wake_one() = 0; + + virtual void wake_all() = 0; + + struct Internal; + +protected: + + Internal* _int; +}; + +#ifndef DBUS_HAS_RECURSIVE_MUTEX +typedef Mutex* (*MutexNewFn)(); +typedef bool (*MutexFreeFn)( Mutex* mx ); +typedef bool (*MutexLockFn)( Mutex* mx ); +typedef void (*MutexUnlockFn)( Mutex* mx ); +#else +typedef Mutex* (*MutexNewFn)(); +typedef void (*MutexFreeFn)( Mutex* mx ); +typedef void (*MutexLockFn)( Mutex* mx ); +typedef void (*MutexUnlockFn)( Mutex* mx ); +#endif//DBUS_HAS_RECURSIVE_MUTEX + +typedef CondVar* (*CondVarNewFn)(); +typedef void (*CondVarFreeFn)( CondVar* cv ); +typedef void (*CondVarWaitFn)( CondVar* cv, Mutex* mx ); +typedef bool (*CondVarWaitTimeoutFn)( CondVar* cv, Mutex* mx, int timeout ); +typedef void (*CondVarWakeOneFn)( CondVar* cv ); +typedef void (*CondVarWakeAllFn)( CondVar* cv ); + +#ifdef DBUS_HAS_THREADS_INIT_DEFAULT +void DXXAPI _init_threading(); +#endif//DBUS_HAS_THREADS_INIT_DEFAULT + +void DXXAPI _init_threading( + MutexNewFn, MutexFreeFn, MutexLockFn, MutexUnlockFn, + CondVarNewFn, CondVarFreeFn, CondVarWaitFn, CondVarWaitTimeoutFn, CondVarWakeOneFn, CondVarWakeAllFn +); + +template +struct Threading +{ + static void init() + { + _init_threading( + mutex_new, mutex_free, mutex_lock, mutex_unlock, + condvar_new, condvar_free, condvar_wait, condvar_wait_timeout, condvar_wake_one, condvar_wake_all + ); + } + + static Mutex* mutex_new() + { + return new Mx; + } + + static void mutex_free( Mutex* mx ) + { + delete mx; + } + + static void mutex_lock( Mutex* mx ) + { + mx->lock(); + } + + static void mutex_unlock( Mutex* mx ) + { + mx->unlock(); + } + + static CondVar* condvar_new() + { + return new Cv; + } + + static void condvar_free( CondVar* cv ) + { + delete cv; + } + + static void condvar_wait( CondVar* cv, Mutex* mx ) + { + cv->wait(mx); + } + + static bool condvar_wait_timeout( CondVar* cv, Mutex* mx, int timeout ) + { + return cv->wait_timeout(mx, timeout); + } + + static void condvar_wake_one( CondVar* cv ) + { + cv->wake_one(); + } + + static void condvar_wake_all( CondVar* cv ) + { + cv->wake_all(); + } +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_DISPATCHER_H Index: /branches/libffado-2.0/external/dbus/include/dbus-c++/debug.h =================================================================== --- /branches/libffado-2.0/external/dbus/include/dbus-c++/debug.h (revision 562) +++ /branches/libffado-2.0/external/dbus/include/dbus-c++/debug.h (revision 562) @@ -0,0 +1,42 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_DEBUG_H +#define __DBUSXX_DEBUG_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "api.h" + +namespace DBus { + +typedef void (*LogFunction)(const char* format, ...); + +extern DXXAPI LogFunction debug_log; + +} /* namespace DBus */ + +#endif Index: /branches/libffado-2.0/external/dbus/SConscript =================================================================== --- /branches/libffado-2.0/external/dbus/SConscript (revision 1185) +++ /branches/libffado-2.0/external/dbus/SConscript (revision 1185) @@ -0,0 +1,82 @@ +# +# Copyright (C) 2007 Arnold Krille +# Copyright (C) 2007 Pieter Palmers +# +# 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 . +# + +import os +import sys + +Import( 'env' ) +dbus_env = env.Clone() + +if dbus_env.has_key('DBUS1_FLAGS'): + dbus_env.MergeFlags( dbus_env['DBUS1_FLAGS'] ) + +# add the local version of libdbus++ +dbus_env.AppendUnique( CPPPATH=["#/external/dbus/include"] ) +dbus_env.AppendUnique( LIBPATH=[dbus_env['build_base']+"external/dbus"]) +dbus_env.AppendUnique( LIBS=["dbus-c++"] ) +dbus_env.AppendUnique( CCFLAGS=["-DDBUS_API_SUBJECT_TO_CHANGE"] ) + +sources = [ + 'src/connection.cpp', + 'src/debug.cpp', + 'src/dispatcher.cpp', + 'src/error.cpp', + 'src/eventloop.cpp', + 'src/interface.cpp', + 'src/introspection.cpp', + 'src/property.cpp', + 'src/message.cpp', + 'src/object.cpp', + 'src/pendingcall.cpp', + 'src/server.cpp', + 'src/types.cpp' +] + +if dbus_env.has_key('DEBUG') and dbus_env['DEBUG']: + dbus_env.AppendUnique( CCFLAGS=["-DDEBUG","-g"] ) + +dbus_env.PrependUnique( LIBS=["expat"] ) +libdbuspp=dbus_env.StaticLibrary('dbus-c++', sources) + +# +# tools +# + +tools_env = dbus_env + +introspect_sources = [ + 'tools/introspect.cpp', +] + +xml2cpp_sources = [ + 'tools/xml.cpp','tools/xml2cpp.cpp' +] + +tools_env.AppendUnique( CCFLAGS=["-DDBUS_API_SUBJECT_TO_CHANGE"] ) +tools_env.AppendUnique( CPPPATH=["#/external/dbus/include"] ) +tools_env.PrependUnique( LIBPATH=dbus_env['build_base']+"external/dbus" ) +tools_env.PrependUnique( LIBS="dbus-c++" ) + +dbusxx_introspect = tools_env.Program('dbusxx-introspect', introspect_sources) +dbusxx_xml2cpp = tools_env.Program('dbusxx-xml2cpp', xml2cpp_sources) + Index: /branches/libffado-2.0/external/dbus/src/glib-integration.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/src/glib-integration.cpp (revision 562) +++ /branches/libffado-2.0/external/dbus/src/glib-integration.cpp (revision 562) @@ -0,0 +1,218 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include + +#include // for DBUS_WATCH_* + +using namespace DBus; + +Glib::BusTimeout::BusTimeout( Timeout::Internal* ti, GMainContext* ctx ) +: Timeout(ti), _ctx(ctx) +{ + _enable(); +} + +Glib::BusTimeout::~BusTimeout() +{ + _disable(); +} + +void Glib::BusTimeout::toggle() +{ + debug_log("glib: timeout %p toggled (%s)", this, Timeout::enabled() ? "on":"off"); + + if(Timeout::enabled()) _enable(); + else _disable(); +} + +gboolean Glib::BusTimeout::timeout_handler( gpointer data ) +{ + Glib::BusTimeout* t = reinterpret_cast(data); + + t->handle(); + + return TRUE; +} + +void Glib::BusTimeout::_enable() +{ + _source = g_timeout_source_new(Timeout::interval()); + g_source_set_callback(_source, timeout_handler, this, NULL); + g_source_attach(_source, _ctx); +} + +void Glib::BusTimeout::_disable() +{ + g_source_destroy(_source); +} + +struct BusSource +{ + GSource source; + GPollFD poll; +}; + +static gboolean watch_prepare( GSource *source, gint *timeout ) +{ +// debug_log("glib: watch_prepare"); + + *timeout = -1; + return FALSE; +} + +static gboolean watch_check( GSource *source ) +{ +// debug_log("glib: watch_check"); + + BusSource* io = (BusSource*)source; + return io->poll.revents ? TRUE : FALSE; +} + +static gboolean watch_dispatch( GSource *source, GSourceFunc callback, gpointer data ) +{ + debug_log("glib: watch_dispatch"); + + gboolean cb = callback(data); + DBus::default_dispatcher->dispatch_pending(); //TODO: won't work in case of multiple dispatchers + return cb; +} + +static GSourceFuncs watch_funcs = { + watch_prepare, + watch_check, + watch_dispatch, + NULL +}; + +Glib::BusWatch::BusWatch( Watch::Internal* wi, GMainContext* ctx ) +: Watch(wi), _ctx(ctx) +{ + _enable(); +} + +Glib::BusWatch::~BusWatch() +{ + _disable(); +} + +void Glib::BusWatch::toggle() +{ + debug_log("glib: watch %p toggled (%s)", this, Watch::enabled() ? "on":"off"); + + if(Watch::enabled()) _enable(); + else _disable(); +} + +gboolean Glib::BusWatch::watch_handler( gpointer data ) +{ + Glib::BusWatch* w = reinterpret_cast(data); + + BusSource* io = (BusSource*)(w->_source); + + int flags = 0; + if(io->poll.revents & G_IO_IN) + flags |= DBUS_WATCH_READABLE; + if(io->poll.revents & G_IO_OUT) + flags |= DBUS_WATCH_WRITABLE; + if(io->poll.revents & G_IO_ERR) + flags |= DBUS_WATCH_ERROR; + if(io->poll.revents & G_IO_HUP) + flags |= DBUS_WATCH_HANGUP; + + w->handle(flags); + + return TRUE; +} + +void Glib::BusWatch::_enable() +{ + _source = g_source_new(&watch_funcs, sizeof(BusSource)); + g_source_set_callback(_source, watch_handler, this, NULL); + + int flags = Watch::flags(); + int condition = 0; + + if(flags & DBUS_WATCH_READABLE) + condition |= G_IO_IN; +// if(flags & DBUS_WATCH_WRITABLE) +// condition |= G_IO_OUT; + if(flags & DBUS_WATCH_ERROR) + condition |= G_IO_ERR; + if(flags & DBUS_WATCH_HANGUP) + condition |= G_IO_HUP; + + GPollFD* poll = &(((BusSource*)_source)->poll); + poll->fd = Watch::descriptor(); + poll->events = condition; + poll->revents = 0; + + g_source_add_poll(_source, poll); + g_source_attach(_source, _ctx); +} + +void Glib::BusWatch::_disable() +{ + GPollFD* poll = &(((BusSource*)_source)->poll); + g_source_remove_poll(_source, poll); + g_source_destroy(_source); +} + +void Glib::BusDispatcher::attach( GMainContext* ctx ) +{ + _ctx = ctx ? ctx : g_main_context_default(); +} + +Timeout* Glib::BusDispatcher::add_timeout( Timeout::Internal* wi ) +{ + Timeout* t = new Glib::BusTimeout(wi, _ctx); + + debug_log("glib: added timeout %p (%s)", t, t->enabled() ? "on":"off"); + + return t; +} + +void Glib::BusDispatcher::rem_timeout( Timeout* t ) +{ + debug_log("glib: removed timeout %p", t); + + delete t; +} + +Watch* Glib::BusDispatcher::add_watch( Watch::Internal* wi ) +{ + Watch* w = new Glib::BusWatch(wi, _ctx); + + debug_log("glib: added watch %p (%s) fd=%d flags=%d", + w, w->enabled() ? "on":"off", w->descriptor(), w->flags() + ); + return w; +} + +void Glib::BusDispatcher::rem_watch( Watch* w ) +{ + debug_log("glib: removed watch %p", w); + + delete w; +} Index: /branches/libffado-2.0/external/dbus/src/connection.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/src/connection.cpp (revision 562) +++ /branches/libffado-2.0/external/dbus/src/connection.cpp (revision 562) @@ -0,0 +1,413 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include +#include + +#include +#include + +#include "internalerror.h" + +#include "connection_p.h" +#include "dispatcher_p.h" +#include "server_p.h" +#include "message_p.h" +#include "pendingcall_p.h" + +using namespace DBus; + +Connection::Private::Private( DBusConnection* c, Server::Private* s ) +: conn(c) , dispatcher(0), server(s) +{ + init(); +} + +Connection::Private::Private( DBusBusType type ) +{ + InternalError e; + + conn = dbus_bus_get_private(type, e); + + if(e) throw Error(e); + + init(); +} + +Connection::Private::~Private() +{ + debug_log("terminating connection 0x%08x", conn); + + detach_server(); + + if(dbus_connection_get_is_connected(conn)) + { + std::vector::iterator i = names.begin(); + + while(i != names.end()) + { + debug_log("%s: releasing bus name %s", dbus_bus_get_unique_name(conn), i->c_str()); + dbus_bus_release_name(conn, i->c_str(), NULL); + ++i; + } + dbus_connection_close(conn); + } + dbus_connection_unref(conn); +} + +void Connection::Private::init() +{ + dbus_connection_ref(conn); + dbus_connection_ref(conn); //todo: the library has to own another reference + + disconn_filter = new Callback( + this, &Connection::Private::disconn_filter_function + ); + + dbus_connection_add_filter(conn, message_filter_stub, &disconn_filter, NULL); + + dbus_connection_set_dispatch_status_function(conn, dispatch_status_stub, this, 0); + dbus_connection_set_exit_on_disconnect(conn, false); //why was this set to true?? +} + +void Connection::Private::detach_server() +{ +/* Server::Private* tmp = server; + + server = NULL; + + if(tmp) + { + ConnectionList::iterator i; + + for(i = tmp->connections.begin(); i != tmp->connections.end(); ++i) + { + if(i->_pvt.get() == this) + { + tmp->connections.erase(i); + break; + } + } + }*/ +} + +bool Connection::Private::do_dispatch() +{ + debug_log("dispatching on %p", conn); + + if(!dbus_connection_get_is_connected(conn)) + { + debug_log("connection terminated"); + + detach_server(); + + return true; + } + + return dbus_connection_dispatch(conn) != DBUS_DISPATCH_DATA_REMAINS; +} + +void Connection::Private::dispatch_status_stub( DBusConnection* dc, DBusDispatchStatus status, void* data ) +{ + Private* p = static_cast(data); + + switch(status) + { + case DBUS_DISPATCH_DATA_REMAINS: + debug_log("some dispatching to do on %p", dc); + p->dispatcher->queue_connection(p); + break; + + case DBUS_DISPATCH_COMPLETE: + debug_log("all dispatching done on %p", dc); + break; + + case DBUS_DISPATCH_NEED_MEMORY: //uh oh... + debug_log("connection %p needs memory", dc); + break; + } +} + +DBusHandlerResult Connection::Private::message_filter_stub( DBusConnection* conn, DBusMessage* dmsg, void* data ) +{ + MessageSlot* slot = static_cast(data); + + Message msg = Message(new Message::Private(dmsg)); + + return slot && !slot->empty() && slot->call(msg) + ? DBUS_HANDLER_RESULT_HANDLED + : DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +bool Connection::Private::disconn_filter_function( const Message& msg ) +{ + if(msg.is_signal(DBUS_INTERFACE_LOCAL,"Disconnected")) + { + debug_log("%p disconnected by local bus", conn); + dbus_connection_close(conn); + + return true; + } + return false; +} + +Connection Connection::SystemBus() +{ + return Connection(new Private(DBUS_BUS_SYSTEM)); +} + +Connection Connection::SessionBus() +{ + return Connection(new Private(DBUS_BUS_SESSION)); +} + +Connection Connection::ActivationBus() +{ + return Connection(new Private(DBUS_BUS_STARTER)); +} + +Connection::Connection( const char* address, bool priv ) +{ + InternalError e; + DBusConnection* conn = priv + ? dbus_connection_open_private(address, e) + : dbus_connection_open(address, e); + + if(e) throw Error(e); + + _pvt = new Private(conn); + + setup(default_dispatcher); + + debug_log("connected to %s", address); +} + +Connection::Connection( Connection::Private* p ) +: _pvt(p) +{ + setup(default_dispatcher); +} + +Connection::Connection( const Connection& c ) +: _pvt(c._pvt) +{ + dbus_connection_ref(_pvt->conn); +} + +Connection::~Connection() +{ + dbus_connection_unref(_pvt->conn); +} + +Dispatcher* Connection::setup( Dispatcher* dispatcher ) +{ + debug_log("registering stubs for connection %p", _pvt->conn); + + if(!dispatcher) dispatcher = default_dispatcher; + + if(!dispatcher) throw ErrorFailed("no default dispatcher set for new connection"); + + Dispatcher* prev = _pvt->dispatcher; + + _pvt->dispatcher = dispatcher; + + dispatcher->queue_connection(_pvt.get()); + + dbus_connection_set_watch_functions( + _pvt->conn, + Dispatcher::Private::on_add_watch, + Dispatcher::Private::on_rem_watch, + Dispatcher::Private::on_toggle_watch, + dispatcher, + 0 + ); + + dbus_connection_set_timeout_functions( + _pvt->conn, + Dispatcher::Private::on_add_timeout, + Dispatcher::Private::on_rem_timeout, + Dispatcher::Private::on_toggle_timeout, + dispatcher, + 0 + ); + + return prev; +} + +bool Connection::operator == ( const Connection& c ) const +{ + return _pvt->conn == c._pvt->conn; +} + +bool Connection::register_bus() +{ + InternalError e; + + bool r = dbus_bus_register(_pvt->conn, e); + + if(e) throw (e); + + return r; +} + +bool Connection::connected() const +{ + return dbus_connection_get_is_connected(_pvt->conn); +} + +void Connection::disconnect() +{ +// dbus_connection_disconnect(_pvt->conn); // disappeared in 0.9x + dbus_connection_close(_pvt->conn); +} + +void Connection::exit_on_disconnect( bool exit ) +{ + dbus_connection_set_exit_on_disconnect(_pvt->conn, exit); +} + +bool Connection::unique_name( const char* n ) +{ + return dbus_bus_set_unique_name(_pvt->conn, n); +} + +const char* Connection::unique_name() const +{ + return dbus_bus_get_unique_name(_pvt->conn); +} + +void Connection::flush() +{ + dbus_connection_flush(_pvt->conn); +} + +void Connection::add_match( const char* rule ) +{ + InternalError e; + + dbus_bus_add_match(_pvt->conn, rule, e); + + debug_log("%s: added match rule %s", unique_name(), rule); + + if(e) throw Error(e); +} + +void Connection::remove_match( const char* rule ) +{ + InternalError e; + + dbus_bus_remove_match(_pvt->conn, rule, e); + + debug_log("%s: removed match rule %s", unique_name(), rule); + + if(e) throw Error(e); +} + +bool Connection::add_filter( MessageSlot& s ) +{ + debug_log("%s: adding filter", unique_name()); + return dbus_connection_add_filter(_pvt->conn, Private::message_filter_stub, &s, NULL); +} + +void Connection::remove_filter( MessageSlot& s ) +{ + debug_log("%s: removing filter", unique_name()); + dbus_connection_remove_filter(_pvt->conn, Private::message_filter_stub, &s); +} + +bool Connection::send( const Message& msg, unsigned int* serial ) +{ + return dbus_connection_send(_pvt->conn, msg._pvt->msg, serial); +} + +Message Connection::send_blocking( Message& msg, int timeout ) +{ + DBusMessage* reply; + InternalError e; + + reply = dbus_connection_send_with_reply_and_block(_pvt->conn, msg._pvt->msg, timeout, e); + + if(e) throw Error(e); + + return Message(new Message::Private(reply), false); +} + +PendingCall Connection::send_async( Message& msg, int timeout ) +{ + DBusPendingCall* pending; + + if(!dbus_connection_send_with_reply(_pvt->conn, msg._pvt->msg, &pending, timeout)) + { + throw ErrorNoMemory("Unable to start asynchronous call"); + } + return PendingCall(new PendingCall::Private(pending)); +} + +void Connection::request_name( const char* name, int flags ) +{ + InternalError e; + + debug_log("%s: registering bus name %s", unique_name(), name); + + dbus_bus_request_name(_pvt->conn, name, flags, e); //we deliberately don't check return value + + if(e) throw Error(e); + +// this->remove_match("destination"); + + if(name) + { + _pvt->names.push_back(name); + std::string match = "destination='" + _pvt->names.back() + "'"; + add_match(match.c_str()); + } +} + +bool Connection::has_name( const char* name ) +{ + InternalError e; + + bool b = dbus_bus_name_has_owner(_pvt->conn, name, e); + + if(e) throw Error(e); + + return b; +} + +const std::vector& Connection::names() +{ + return _pvt->names; +} + +bool Connection::start_service( const char* name, unsigned long flags ) +{ + InternalError e; + + bool b = dbus_bus_start_service_by_name(_pvt->conn, name, flags, NULL, e); + + if(e) throw Error(e); + + return b; +} + Index: /branches/libffado-2.0/external/dbus/src/introspection.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/src/introspection.cpp (revision 575) +++ /branches/libffado-2.0/external/dbus/src/introspection.cpp (revision 575) @@ -0,0 +1,185 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include +#include +#include + +#include + +#include + +using namespace DBus; + +static const char* introspectable_name = "org.freedesktop.DBus.Introspectable"; + +IntrospectableAdaptor::IntrospectableAdaptor() +: InterfaceAdaptor(introspectable_name) +{ + register_method(IntrospectableAdaptor, Introspect, Introspect); +} + +Message IntrospectableAdaptor::Introspect( const CallMessage& call ) +{ + debug_log("requested introspection data"); + + std::ostringstream xml; + + xml << DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE; + + const std::string path = object()->path(); + + xml << ""; + + InterfaceAdaptorTable::const_iterator iti; + + for(iti = _interfaces.begin(); iti != _interfaces.end(); ++iti) + { + debug_log("introspecting interface %s", iti->first.c_str()); + + IntrospectedInterface* const intro = iti->second->introspect(); + if(intro) + { + xml << "name << "\">"; + + for(const IntrospectedProperty* p = intro->properties; p->name; ++p) + { + std::string access; + + if(p->read) access += "read"; + if(p->write) access += "write"; + + xml << "name << "\"" + << " type=\"" << p->type << "\"" + << " access=\"" << access << "\"/>"; + } + + for(const IntrospectedMethod* m = intro->methods; m->args; ++m) + { + xml << "name << "\">"; + + for(const IntrospectedArgument* a = m->args; a->type; ++a) + { + xml << "in ? "in" : "out") << "\"" + << " type=\"" << a->type << "\""; + + if(a->name) xml << " name=\"" << a->name << "\""; + + xml << "/>"; + } + + xml << ""; + } + + for(const IntrospectedMethod* m = intro->signals; m->args; ++m) + { + xml << "name << "\">"; + + for(const IntrospectedArgument* a = m->args; a->type; ++a) + { + xml << "type << "\""; + + if(a->name) xml << " name=\"" << a->name << "\""; + + xml << "/>"; + } + xml << ""; + } + + xml << ""; + } + } + const ObjectAdaptorPList children = ObjectAdaptor::from_path_prefix(path + '/'); + + ObjectAdaptorPList::const_iterator oci; + + debug_log("nb children: %d", children.size()); + for(oci = children.begin(); oci != children.end(); ++oci) + { + + std::string name = (*oci)->path().substr(path.length()+1); + + std::string::size_type loc = name.find('/'); + if( loc != std::string::npos ) { + name.substr(loc); + } + + xml << ""; + } + + xml << ""; + + ReturnMessage reply(call); + MessageIter wi = reply.writer(); + wi.append_string(xml.str().c_str()); + return reply; +} + +IntrospectedInterface* const IntrospectableAdaptor::introspect() const +{ + static IntrospectedArgument Introspect_args[] = + { + { "data", "s", false }, + { 0, 0, 0 } + }; + static IntrospectedMethod Introspectable_methods[] = + { + { "Introspect", Introspect_args }, + { 0, 0 } + }; + static IntrospectedMethod Introspectable_signals[] = + { + { 0, 0 } + }; + static IntrospectedProperty Introspectable_properties[] = + { + { 0, 0, 0, 0 } + }; + static IntrospectedInterface Introspectable_interface = + { + introspectable_name, + Introspectable_methods, + Introspectable_signals, + Introspectable_properties + }; + return &Introspectable_interface; +} + +IntrospectableProxy::IntrospectableProxy() +: InterfaceProxy(introspectable_name) +{} + +std::string IntrospectableProxy::Introspect() +{ + DBus::CallMessage call; + + call.member("Introspect"); + + DBus::Message ret = invoke_method(call); + + DBus::MessageIter ri = ret.reader(); + const char* str = ri.get_string(); + + return str; +} Index: /branches/libffado-2.0/external/dbus/src/interface.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/src/interface.cpp (revision 562) +++ /branches/libffado-2.0/external/dbus/src/interface.cpp (revision 562) @@ -0,0 +1,148 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include +#include + +#include "internalerror.h" + +using namespace DBus; + +Interface::Interface( const std::string& name ) +: _name(name) +{} + +Interface::~Interface() +{} + +InterfaceAdaptor* AdaptorBase::find_interface( const std::string& name ) +{ + InterfaceAdaptorTable::const_iterator ii = _interfaces.find(name); + + return ii != _interfaces.end() ? ii->second : NULL; +} + +InterfaceAdaptor::InterfaceAdaptor( const std::string& name ) +: Interface(name) +{ + debug_log("adding interface %s", name.c_str()); + + _interfaces[name] = this; +} + +Message InterfaceAdaptor::dispatch_method( const CallMessage& msg ) +{ + const char* name = msg.member(); + + MethodTable::iterator mi = _methods.find(name); + if( mi != _methods.end() ) + { + return mi->second.call( msg ); + } + else + { + return ErrorMessage(msg, DBUS_ERROR_UNKNOWN_METHOD, name); + } +} + +void InterfaceAdaptor::emit_signal( const SignalMessage& sig ) +{ + SignalMessage& sig2 = const_cast(sig); + + sig2.interface( name().c_str() ); + _emit_signal(sig2); +} + +Variant* InterfaceAdaptor::get_property( const std::string& name ) +{ + PropertyTable::iterator pti = _properties.find(name); + + if( pti != _properties.end() ) + { + if( !pti->second.read ) + throw ErrorAccessDenied("property is not readable"); + + return &(pti->second.value); + } + return NULL; +} + +void InterfaceAdaptor::set_property( const std::string& name, Variant& value ) +{ + PropertyTable::iterator pti = _properties.find(name); + + if( pti != _properties.end() ) + { + if( !pti->second.write ) + throw ErrorAccessDenied("property is not writeable"); + + Signature sig = value.signature(); + + if( pti->second.sig != sig ) + throw ErrorInvalidSignature("property expects a different type"); + + pti->second.value = value; + return; + } + throw ErrorFailed("requested property not found"); +} + +InterfaceProxy* ProxyBase::find_interface( const std::string& name ) +{ + InterfaceProxyTable::const_iterator ii = _interfaces.find(name); + + return ii != _interfaces.end() ? ii->second : NULL; +} + +InterfaceProxy::InterfaceProxy( const std::string& name ) +: Interface(name) +{ + debug_log("adding interface %s", name.c_str()); + + _interfaces[name] = this; +} + +bool InterfaceProxy::dispatch_signal( const SignalMessage& msg ) +{ + const char* name = msg.member(); + + SignalTable::iterator si = _signals.find(name); + if( si != _signals.end() ) + { + si->second.call( msg ); + return true; + } + else + { + return false; + } +} + +Message InterfaceProxy::invoke_method( const CallMessage& call ) +{ + CallMessage& call2 = const_cast(call); + + call2.interface( name().c_str() ); + return _invoke_method(call2); +} Index: /branches/libffado-2.0/external/dbus/src/pendingcall_p.h =================================================================== --- /branches/libffado-2.0/external/dbus/src/pendingcall_p.h (revision 562) +++ /branches/libffado-2.0/external/dbus/src/pendingcall_p.h (revision 562) @@ -0,0 +1,53 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_PENDING_CALL_P_H +#define __DBUSXX_PENDING_CALL_P_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +namespace DBus { + +struct DXXAPILOCAL PendingCall::Private +{ + DBusPendingCall* call; + int dataslot; + + Private( DBusPendingCall* ); + + ~Private(); + + static void notify_stub( DBusPendingCall* dpc, void* data ); +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_PENDING_CALL_P_H Index: /branches/libffado-2.0/external/dbus/src/types.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/src/types.cpp (revision 562) +++ /branches/libffado-2.0/external/dbus/src/types.cpp (revision 562) @@ -0,0 +1,102 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include +#include +#include +#include + +#include "message_p.h" +#include "internalerror.h" + +using namespace DBus; + +Variant::Variant() +: _msg(CallMessage()) // dummy message used as temporary storage for variant data +{ +} + +Variant::Variant( MessageIter& it ) +: _msg(CallMessage()) +{ + MessageIter vi = it.recurse(); + MessageIter mi = _msg.writer(); + vi.copy_data(mi); +} + +Variant& Variant::operator = ( const Variant& v ) +{ + if(&v != this) + { + _msg = v._msg; + } + return *this; +} + +void Variant::clear() +{ + CallMessage empty; + _msg = empty; +} + +const Signature Variant::signature() const +{ + char* sigbuf = reader().signature(); + + Signature signature = sigbuf; + + free(sigbuf); + + return signature; +} + +MessageIter& operator << ( MessageIter& iter, const Variant& val ) +{ + const Signature sig = val.signature(); + + MessageIter rit = val.reader(); + MessageIter wit = iter.new_variant(sig.c_str()); + + rit.copy_data(wit); + + iter.close_container(wit); + + return iter; +} + +MessageIter& operator >> ( MessageIter& iter, Variant& val ) +{ + if(iter.type() != DBUS_TYPE_VARIANT) + throw ErrorInvalidArgs("variant type expected"); + + val.clear(); + + MessageIter vit = iter.recurse(); + MessageIter mit = val.writer(); + + vit.copy_data(mit); + + return ++iter; +} + Index: /branches/libffado-2.0/external/dbus/src/object.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/src/object.cpp (revision 562) +++ /branches/libffado-2.0/external/dbus/src/object.cpp (revision 562) @@ -0,0 +1,352 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include +#include +#include "internalerror.h" + +#include +#include + +#include "message_p.h" +#include "server_p.h" +#include "connection_p.h" + +using namespace DBus; + +Object::Object( Connection& conn, const Path& path, const char* service ) +: _conn(conn), _path(path), _service(service ? service : "") +{ +} + +Object::~Object() +{ +} + +struct ObjectAdaptor::Private +{ + static void unregister_function_stub( DBusConnection*, void* ); + static DBusHandlerResult message_function_stub( DBusConnection*, DBusMessage*, void* ); +}; + +static DBusObjectPathVTable _vtable = +{ + ObjectAdaptor::Private::unregister_function_stub, + ObjectAdaptor::Private::message_function_stub, + NULL, NULL, NULL, NULL +}; + +void ObjectAdaptor::Private::unregister_function_stub( DBusConnection* conn, void* data ) +{ + //TODO: what do we have to do here ? +} + +DBusHandlerResult ObjectAdaptor::Private::message_function_stub( DBusConnection*, DBusMessage* dmsg, void* data ) +{ + ObjectAdaptor* o = static_cast(data); + + if( o ) + { + Message msg(new Message::Private(dmsg)); + + debug_log("in object %s", o->path().c_str()); + debug_log(" got message #%d from %s to %s", + msg.serial(), + msg.sender(), + msg.destination() + ); + + return o->handle_message(msg) + ? DBUS_HANDLER_RESULT_HANDLED + : DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + else + { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } +} + +typedef std::map ObjectAdaptorTable; +static ObjectAdaptorTable _adaptor_table; + +ObjectAdaptor* ObjectAdaptor::from_path( const Path& path ) +{ + ObjectAdaptorTable::iterator ati = _adaptor_table.find(path); + + if(ati != _adaptor_table.end()) + return ati->second; + + return NULL; +} + +ObjectAdaptorPList ObjectAdaptor::from_path_prefix( const std::string& prefix ) +{ + ObjectAdaptorPList ali; + + ObjectAdaptorTable::iterator ati = _adaptor_table.begin(); + + size_t plen = prefix.length(); + + while(ati != _adaptor_table.end()) + { + if(!strncmp(ati->second->path().c_str(), prefix.c_str(), plen)) + ali.push_back(ati->second); + + ++ati; + } + + return ali; +} + +ObjectAdaptor::ObjectAdaptor( Connection& conn, const Path& path ) +: Object(conn, path, conn.unique_name()) +{ + register_obj(); +} + +ObjectAdaptor::~ObjectAdaptor() +{ + unregister_obj(); +} + +void ObjectAdaptor::register_obj() +{ + debug_log("registering local object %s", path().c_str()); + + if(!dbus_connection_register_object_path(conn()._pvt->conn, path().c_str(), &_vtable, this)) + { + throw ErrorNoMemory("unable to register object path"); + } + else + { + InterfaceAdaptorTable::const_iterator ii = _interfaces.begin(); + while( ii != _interfaces.end() ) + { + std::string im = "type='method_call',interface='"+ii->first+"',path='"+path()+"'"; + conn().add_match(im.c_str()); + ++ii; + } + } + + _adaptor_table[path()] = this; +} + +void ObjectAdaptor::unregister_obj() +{ + _adaptor_table.erase(path()); + + debug_log("unregistering local object %s", path().c_str()); + + dbus_connection_unregister_object_path(conn()._pvt->conn, path().c_str()); + + InterfaceAdaptorTable::const_iterator ii = _interfaces.begin(); + while( ii != _interfaces.end() ) + { + std::string im = "type='method_call',interface='"+ii->first+"',path='"+path()+"'"; + conn().remove_match(im.c_str()); + ++ii; + } +} + +void ObjectAdaptor::_emit_signal( SignalMessage& sig ) +{ + sig.path(path().c_str()); + + conn().send(sig); +} + +struct ReturnLaterError +{ + const Tag* tag; +}; + +bool ObjectAdaptor::handle_message( const Message& msg ) +{ + switch( msg.type() ) + { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + { + const CallMessage& cmsg = reinterpret_cast(msg); + const char* member = cmsg.member(); + const char* interface = cmsg.interface(); + + debug_log(" invoking method %s.%s", interface, member); + + InterfaceAdaptor* ii = find_interface(interface); + if( ii ) + { + try + { + Message ret = ii->dispatch_method(cmsg); + conn().send(ret); + } + catch(Error& e) + { + ErrorMessage em(cmsg, e.name(), e.message()); + conn().send(em); + } + catch(ReturnLaterError& rle) + { + _continuations[rle.tag] = new Continuation(conn(), cmsg, rle.tag); + } + return true; + } + else + { + return false; + } + } + default: + { + return false; + } + } +} + +void ObjectAdaptor::return_later( const Tag* tag ) +{ + ReturnLaterError rle = { tag }; + throw rle; +} + +void ObjectAdaptor::return_now( Continuation* ret ) +{ + ret->_conn.send(ret->_return); + + ContinuationMap::iterator di = _continuations.find(ret->_tag); + + delete di->second; + + _continuations.erase(di); +} + +void ObjectAdaptor::return_error( Continuation* ret, const Error error ) +{ + ret->_conn.send(ErrorMessage(ret->_call, error.name(), error.message())); + + ContinuationMap::iterator di = _continuations.find(ret->_tag); + + delete di->second; + + _continuations.erase(di); +} + +ObjectAdaptor::Continuation* ObjectAdaptor::find_continuation( const Tag* tag ) +{ + ContinuationMap::iterator di = _continuations.find(tag); + + return di != _continuations.end() ? di->second : NULL; +} + +ObjectAdaptor::Continuation::Continuation( Connection& conn, const CallMessage& call, const Tag* tag ) +: _conn(conn), _call(call), _return(_call), _tag(tag) +{ + _writer = _return.writer(); //todo: verify +} + +/* +*/ + +ObjectProxy::ObjectProxy( Connection& conn, const Path& path, const char* service ) +: Object(conn, path, service) +{ + register_obj(); +} + +ObjectProxy::~ObjectProxy() +{ + unregister_obj(); +} + +void ObjectProxy::register_obj() +{ + debug_log("registering remote object %s", path().c_str()); + + _filtered = new Callback(this, &ObjectProxy::handle_message); + + conn().add_filter(_filtered); + + InterfaceProxyTable::const_iterator ii = _interfaces.begin(); + while( ii != _interfaces.end() ) + { + std::string im = "type='signal',interface='"+ii->first+"',path='"+path()+"'"; + conn().add_match(im.c_str()); + ++ii; + } +} + +void ObjectProxy::unregister_obj() +{ + debug_log("unregistering remote object %s", path().c_str()); + + InterfaceProxyTable::const_iterator ii = _interfaces.begin(); + while( ii != _interfaces.end() ) + { + std::string im = "type='signal',interface='"+ii->first+"',path='"+path()+"'"; + conn().remove_match(im.c_str()); + ++ii; + } + conn().remove_filter(_filtered); +} + +Message ObjectProxy::_invoke_method( CallMessage& call ) +{ + call.path(path().c_str()); + call.destination(service().c_str()); + + return conn().send_blocking(call, 2000); +} + +bool ObjectProxy::handle_message( const Message& msg ) +{ + switch( msg.type() ) + { + case DBUS_MESSAGE_TYPE_SIGNAL: + { + const SignalMessage& smsg = reinterpret_cast(msg); + const char* interface = smsg.interface(); + const char* member = smsg.member(); + const char* objpath = smsg.path(); + + if( objpath != path() ) return false; + + debug_log("filtered signal %s(in %s) from %s to object %s", + member, interface, msg.sender(), objpath); + + InterfaceProxy* ii = find_interface(interface); + if( ii ) + { + return ii->dispatch_signal(smsg); + } + else + { + return false; + } + } + default: + { + return false; + } + } +} Index: /branches/libffado-2.0/external/dbus/src/server.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/src/server.cpp (revision 562) +++ /branches/libffado-2.0/external/dbus/src/server.cpp (revision 562) @@ -0,0 +1,126 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include +#include + +#include "internalerror.h" +#include "server_p.h" +#include "connection_p.h" +#include "dispatcher_p.h" + +using namespace DBus; + +Server::Private::Private( DBusServer* s ) +: server(s) +{ +} + +Server::Private::~Private() +{ +} + +void Server::Private::on_new_conn_cb( DBusServer* server, DBusConnection* conn, void* data ) +{ + Server* s = static_cast(data); + + Connection nc (new Connection::Private(conn, s->_pvt.get())); + + s->_pvt->connections.push_back(nc); + + s->on_new_connection(nc); + + debug_log("incoming connection 0x%08x", conn); +} + +Server::Server( const char* address ) +{ + InternalError e; + DBusServer* server = dbus_server_listen(address, e); + + if(e) throw Error(e); + + debug_log("server 0x%08x listening on %s", server, address); + + _pvt = new Private(server); + + dbus_server_set_new_connection_function(_pvt->server, Private::on_new_conn_cb, this, NULL); + + setup(default_dispatcher); +} +/* +Server::Server( const Server& s ) +: _pvt(s._pvt) +{ + dbus_server_ref(_pvt->server); +} +*/ +Server::~Server() +{ + dbus_server_unref(_pvt->server); +} + +Dispatcher* Server::setup( Dispatcher* dispatcher ) +{ + debug_log("registering stubs for server %p", _pvt->server); + + Dispatcher* prev = _pvt->dispatcher; + + dbus_server_set_watch_functions( + _pvt->server, + Dispatcher::Private::on_add_watch, + Dispatcher::Private::on_rem_watch, + Dispatcher::Private::on_toggle_watch, + dispatcher, + 0 + ); + + dbus_server_set_timeout_functions( + _pvt->server, + Dispatcher::Private::on_add_timeout, + Dispatcher::Private::on_rem_timeout, + Dispatcher::Private::on_toggle_timeout, + dispatcher, + 0 + ); + + _pvt->dispatcher = dispatcher; + + return prev; +} + +bool Server::operator == ( const Server& s ) const +{ + return _pvt->server == s._pvt->server; +} + +bool Server::listening() const +{ + return dbus_server_get_is_connected(_pvt->server); +} +void Server::disconnect() +{ + dbus_server_disconnect(_pvt->server); +} + Index: /branches/libffado-2.0/external/dbus/src/message_p.h =================================================================== --- /branches/libffado-2.0/external/dbus/src/message_p.h (revision 562) +++ /branches/libffado-2.0/external/dbus/src/message_p.h (revision 562) @@ -0,0 +1,52 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_MESSAGE_P_H +#define __DBUSXX_MESSAGE_P_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +namespace DBus { + +struct DXXAPILOCAL Message::Private +{ + DBusMessage* msg; + + Private() : msg(0) + {} + + Private( DBusMessage* m ) : msg(m) + {} +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_MESSAGE_P_H Index: /branches/libffado-2.0/external/dbus/src/dispatcher_p.h =================================================================== --- /branches/libffado-2.0/external/dbus/src/dispatcher_p.h (revision 562) +++ /branches/libffado-2.0/external/dbus/src/dispatcher_p.h (revision 562) @@ -0,0 +1,57 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_DISPATCHER_P_H +#define __DBUSXX_DISPATCHER_P_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include "internalerror.h" + +namespace DBus { + +struct DXXAPILOCAL Dispatcher::Private +{ + static dbus_bool_t on_add_watch( DBusWatch* watch, void* data ); + + static void on_rem_watch( DBusWatch* watch, void* data ); + + static void on_toggle_watch( DBusWatch* watch, void* data ); + + static dbus_bool_t on_add_timeout( DBusTimeout* timeout, void* data ); + + static void on_rem_timeout( DBusTimeout* timeout, void* data ); + + static void on_toggle_timeout( DBusTimeout* timeout, void* data ); +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_DISPATCHER_P_H Index: /branches/libffado-2.0/external/dbus/src/eventloop.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/src/eventloop.cpp (revision 562) +++ /branches/libffado-2.0/external/dbus/src/eventloop.cpp (revision 562) @@ -0,0 +1,311 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include +#include + +#include +#include + +#include + +using namespace DBus; + +static double millis( timeval tv ) +{ + return (tv.tv_sec*1000.0 + tv.tv_usec/1000.0); +} + +EepleTimeout::EepleTimeout( int interval, bool repeat, EepleMainLoop* ed ) +: _enabled(true), _interval(interval), _repeat(repeat), _expiration(0), _data(0), _disp(ed) +{ + timeval now; + gettimeofday(&now, NULL); + + _expiration = millis(now) + interval; + + _disp->_timeouts.push_back(this); +} + +EepleTimeout::~EepleTimeout() +{ + _disp->_timeouts.remove(this); +} + +EepleWatch::EepleWatch( int fd, int flags, EepleMainLoop* ed ) +: _enabled(true), _fd(fd), _flags(flags), _state(0), _data(0), _disp(ed) +{ + _disp->_watches.push_back(this); +} + +EepleWatch::~EepleWatch() +{ + _disp->_watches.remove(this); +} + +EepleMainLoop::EepleMainLoop() +{ +} + +EepleMainLoop::~EepleMainLoop() +{ + Watches::iterator wi = _watches.begin(); + while(wi != _watches.end()) + { + Watches::iterator wmp = wi; + ++wmp; + delete (*wi); + wi = wmp; + } + + Timeouts::iterator ti = _timeouts.begin(); + while(ti != _timeouts.end()) + { + Timeouts::iterator tmp = ti; + ++tmp; + delete (*ti); + ti = tmp; + } +} + +void EepleMainLoop::dispatch() +{ + int nfd = _watches.size(); + + pollfd fds[nfd]; + + Watches::iterator wi = _watches.begin(); + + for(nfd = 0; wi != _watches.end(); ++wi) + { + if((*wi)->enabled()) + { + fds[nfd].fd = (*wi)->descriptor(); + fds[nfd].events = (*wi)->flags(); + fds[nfd].revents = 0; + + ++nfd; + } + } + + int wait_min = 10000; + + Timeouts::iterator ti; + + for(ti = _timeouts.begin(); ti != _timeouts.end(); ++ti) + { + if((*ti)->enabled() && (*ti)->interval() < wait_min) + wait_min = (*ti)->interval(); + } + + //int rfd = poll(fds, nfd, wait_min); + //if(rfd) debug_log("%d descriptors ready"); + + poll(fds, nfd, wait_min); + + timeval now; + gettimeofday(&now, NULL); + + double now_millis = millis(now); + + ti = _timeouts.begin(); + + while(ti != _timeouts.end()) + { + Timeouts::iterator tmp = ti; + ++tmp; + + if((*ti)->enabled() && now_millis >= (*ti)->_expiration) + { + (*ti)->expired(*(*ti)); + + if((*ti)->_repeat) + { + (*ti)->_expiration = now_millis + (*ti)->_interval; + } + + } + + ti = tmp; + } + + for(int j = 0; j < nfd; ++j) + { + Watches::iterator wi; + + for(wi = _watches.begin(); wi != _watches.end();) + { + Watches::iterator tmp = wi; + ++tmp; + + if((*wi)->enabled() && (*wi)->_fd == fds[j].fd) + { + if(fds[j].revents) + { + (*wi)->_state = fds[j].revents; + + (*wi)->ready(*(*wi)); + + fds[j].revents = 0; + } + } + + wi = tmp; + } + } +} + +/* +*/ + +BusTimeout::BusTimeout( Timeout::Internal* ti, BusDispatcher* bd ) +: Timeout(ti), EepleTimeout(Timeout::interval(), true, bd) +{ + EepleTimeout::enabled(Timeout::enabled()); +} + +void BusTimeout::toggle() +{ + debug_log("timeout %p toggled (%s)", this, Timeout::enabled() ? "on":"off"); + + EepleTimeout::enabled(Timeout::enabled()); +} + +BusWatch::BusWatch( Watch::Internal* wi, BusDispatcher* bd ) +: Watch(wi), EepleWatch(Watch::descriptor(), 0, bd) +{ + int flags = POLLHUP | POLLERR; + + if(Watch::flags() & DBUS_WATCH_READABLE) + flags |= POLLIN; + if(Watch::flags() & DBUS_WATCH_WRITABLE) + flags |= POLLOUT; + + EepleWatch::flags(flags); + EepleWatch::enabled(Watch::enabled()); +} + +void BusWatch::toggle() +{ + debug_log("watch %p toggled (%s)", this, Watch::enabled() ? "on":"off"); + + EepleWatch::enabled(Watch::enabled()); +} + +void BusDispatcher::enter() +{ + debug_log("entering dispatcher %p", this); + + _running = true; + + while(_running) + { + do_iteration(); + } + + debug_log("leaving dispatcher %p", this); +} + +void BusDispatcher::leave() +{ + _running = false; +} + +void BusDispatcher::do_iteration() +{ + dispatch_pending(); + dispatch(); +} + +Timeout* BusDispatcher::add_timeout( Timeout::Internal* ti ) +{ + BusTimeout* bt = new BusTimeout(ti, this); + + bt->expired = new Callback(this, &BusDispatcher::timeout_expired); + bt->data(bt); + + debug_log("added timeout %p (%s)", bt, ((Timeout*)bt)->enabled() ? "on":"off"); + + return bt; +} + +void BusDispatcher::rem_timeout( Timeout* t ) +{ + debug_log("removed timeout %p", t); + + delete t; +} + +Watch* BusDispatcher::add_watch( Watch::Internal* wi ) +{ + BusWatch* bw = new BusWatch(wi, this); + + bw->ready = new Callback(this, &BusDispatcher::watch_ready); + bw->data(bw); + + debug_log("added watch %p (%s) fd=%d flags=%d", + bw, ((Watch*)bw)->enabled() ? "on":"off", ((Watch*)bw)->descriptor(), ((Watch*)bw)->flags() + ); + + return bw; +} + +void BusDispatcher::rem_watch( Watch* w ) +{ + debug_log("removed watch %p", w); + + delete w; +} + +void BusDispatcher::timeout_expired( EepleTimeout& et ) +{ + debug_log("timeout %p expired", &et); + + BusTimeout* timeout = reinterpret_cast(et.data()); + + timeout->handle(); +} + +void BusDispatcher::watch_ready( EepleWatch& ew ) +{ + BusWatch* watch = reinterpret_cast(ew.data()); + + debug_log("watch %p ready, flags=%d state=%d", + watch, ((Watch*)watch)->flags(), watch->state() + ); + + int flags = 0; + + if(watch->state() & POLLIN) + flags |= DBUS_WATCH_READABLE; + if(watch->state() & POLLOUT) + flags |= DBUS_WATCH_WRITABLE; + if(watch->state() & POLLHUP) + flags |= DBUS_WATCH_HANGUP; + if(watch->state() & POLLERR) + flags |= DBUS_WATCH_ERROR; + + watch->handle(flags); +} + Index: /branches/libffado-2.0/external/dbus/src/pendingcall.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/src/pendingcall.cpp (revision 562) +++ /branches/libffado-2.0/external/dbus/src/pendingcall.cpp (revision 562) @@ -0,0 +1,105 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include + +#include + +#include "internalerror.h" +#include "pendingcall_p.h" + +using namespace DBus; + +PendingCall::Private::Private( DBusPendingCall* dpc ) +: call(dpc), dataslot(-1) +{ + if(!dbus_pending_call_allocate_data_slot(&dataslot)) + { + throw ErrorNoMemory("Unable to allocate data slot"); + } +} + +void PendingCall::Private::notify_stub( DBusPendingCall* dpc, void* data ) +{ + PendingCall* pc = static_cast(data); + + pc->slot(*pc); +} + +PendingCall::Private::~Private() +{ + if(dataslot != -1) + { + dbus_pending_call_allocate_data_slot(&dataslot); + } +} + +PendingCall::PendingCall( PendingCall::Private* p ) +: _pvt(p) +{ + if(!dbus_pending_call_set_notify(_pvt->call, Private::notify_stub, this, NULL)) + { + throw ErrorNoMemory("Unable to initialize pending call"); + } +} + +PendingCall::PendingCall( const PendingCall& c ) +: _pvt(c._pvt) +{ + dbus_pending_call_ref(_pvt->call); +} + +PendingCall::~PendingCall() +{ + dbus_pending_call_unref(_pvt->call); +} + +bool PendingCall::completed() +{ + return dbus_pending_call_get_completed(_pvt->call); +} + +void PendingCall::cancel() +{ + dbus_pending_call_cancel(_pvt->call); +} + +void PendingCall::block() +{ + dbus_pending_call_block(_pvt->call); +} + +void PendingCall::data( void* p ) +{ + if(!dbus_pending_call_set_data(_pvt->call, _pvt->dataslot, p, NULL)) + { + throw ErrorNoMemory("Unable to initialize data slot"); + } +} + +void* PendingCall::data() +{ + return dbus_pending_call_get_data(_pvt->call, _pvt->dataslot); +} + Index: /branches/libffado-2.0/external/dbus/src/connection_p.h =================================================================== --- /branches/libffado-2.0/external/dbus/src/connection_p.h (revision 562) +++ /branches/libffado-2.0/external/dbus/src/connection_p.h (revision 562) @@ -0,0 +1,73 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_CONNECTION_P_H +#define __DBUSXX_CONNECTION_P_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include + +#include + +namespace DBus { + +struct DXXAPILOCAL Connection::Private +{ + DBusConnection* conn; + + std::vector names; + + Dispatcher* dispatcher; + bool do_dispatch(); + + MessageSlot disconn_filter; + bool disconn_filter_function( const Message& ); + + Server::Private* server; + void detach_server(); + + Private( DBusConnection*, Server::Private* = NULL ); + + Private( DBusBusType ); + + ~Private(); + + void init(); + + static void dispatch_status_stub( DBusConnection*, DBusDispatchStatus, void* ); + + static DBusHandlerResult message_filter_stub( DBusConnection*, DBusMessage*, void* ); +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_CONNECTION_P_H Index: /branches/libffado-2.0/external/dbus/src/message.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/src/message.cpp (revision 562) +++ /branches/libffado-2.0/external/dbus/src/message.cpp (revision 562) @@ -0,0 +1,638 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include + +#include + +#include "internalerror.h" +#include "message_p.h" + +using namespace DBus; + +/* +*/ + +int MessageIter::type() +{ + return dbus_message_iter_get_arg_type((DBusMessageIter*)&_iter); +} + +bool MessageIter::at_end() +{ + return type() == DBUS_TYPE_INVALID; +} + +bool MessageIter::has_next() +{ + return dbus_message_iter_has_next((DBusMessageIter*)&_iter); +} + +MessageIter& MessageIter::operator ++() +{ + dbus_message_iter_next((DBusMessageIter*)&_iter); + return (*this); +} + +MessageIter MessageIter::operator ++(int) +{ + MessageIter copy(*this); + ++(*this); + return copy; +} + +bool MessageIter::append_basic( int type_id, void* value ) +{ + return dbus_message_iter_append_basic((DBusMessageIter*)&_iter, type_id, value); +} + +void MessageIter::get_basic( int type_id, void* ptr ) +{ + if(type() != type_id) + throw ErrorInvalidArgs("type mismatch"); + + dbus_message_iter_get_basic((DBusMessageIter*)_iter, ptr); +} + +bool MessageIter::append_byte( unsigned char b ) +{ + return append_basic(DBUS_TYPE_BYTE, &b); +} + +unsigned char MessageIter::get_byte() +{ + unsigned char b; + get_basic(DBUS_TYPE_BYTE, &b); + return b; +} + +bool MessageIter::append_bool( bool b ) +{ + dbus_bool_t db = b; + return append_basic(DBUS_TYPE_BOOLEAN, &db); +} + +bool MessageIter::get_bool() +{ + dbus_bool_t db; + get_basic(DBUS_TYPE_BOOLEAN, &db); + return (bool)db; +} + +bool MessageIter::append_int16( signed short i ) +{ + return append_basic(DBUS_TYPE_INT16, &i); +} + +signed short MessageIter::get_int16() +{ + signed short i; + get_basic(DBUS_TYPE_INT16, &i); + return i; +} + +bool MessageIter::append_uint16( unsigned short u ) +{ + return append_basic(DBUS_TYPE_UINT16, &u); +} + +unsigned short MessageIter::get_uint16() +{ + unsigned short u; + get_basic(DBUS_TYPE_UINT16, &u); + return u; +} + +bool MessageIter::append_int32( signed int i ) +{ + return append_basic(DBUS_TYPE_INT32, &i); +} + +signed int MessageIter::get_int32() +{ + signed int i; + get_basic(DBUS_TYPE_INT32, &i); + return i; +} + +bool MessageIter::append_uint32( unsigned int u ) +{ + return append_basic(DBUS_TYPE_UINT32, &u); +} + +unsigned int MessageIter::get_uint32() +{ + unsigned int u; + get_basic(DBUS_TYPE_UINT32, &u); + return u; +} + +signed long long MessageIter::get_int64() +{ + signed long long i; + get_basic(DBUS_TYPE_INT64, &i); + return i; +} + +bool MessageIter::append_int64( signed long long i ) +{ + return append_basic(DBUS_TYPE_INT64, &i); +} + +unsigned long long MessageIter::get_uint64() +{ + unsigned long long u; + get_basic(DBUS_TYPE_UINT64, &u); + return u; +} + +bool MessageIter::append_uint64( unsigned long long u ) +{ + return append_basic(DBUS_TYPE_UINT64, &u); +} + +double MessageIter::get_double() +{ + double d; + get_basic(DBUS_TYPE_DOUBLE, &d); + return d; +} + +bool MessageIter::append_double( double d ) +{ + return append_basic(DBUS_TYPE_DOUBLE, &d); +} + +bool MessageIter::append_string( const char* chars ) +{ + return append_basic(DBUS_TYPE_STRING, &chars); +} + +const char* MessageIter::get_string() +{ + char* chars; + get_basic(DBUS_TYPE_STRING, &chars); + return chars; +} + +bool MessageIter::append_path( const char* chars ) +{ + return append_basic(DBUS_TYPE_OBJECT_PATH, &chars); +} + +const char* MessageIter::get_path() +{ + char* chars; + get_basic(DBUS_TYPE_OBJECT_PATH, &chars); + return chars; +} + +bool MessageIter::append_signature( const char* chars ) +{ + return append_basic(DBUS_TYPE_SIGNATURE, &chars); +} + +const char* MessageIter::get_signature() +{ + char* chars; + get_basic(DBUS_TYPE_SIGNATURE, &chars); + return chars; +} + +MessageIter MessageIter::recurse() +{ + MessageIter iter(msg()); + dbus_message_iter_recurse((DBusMessageIter*)&_iter, (DBusMessageIter*)&(iter._iter)); + return iter; +} + +char* MessageIter::signature() const +{ + return dbus_message_iter_get_signature((DBusMessageIter*)&_iter); +} + +bool MessageIter::append_array( char type, const void* ptr, size_t length ) +{ + return dbus_message_iter_append_fixed_array((DBusMessageIter*)&_iter, type, &ptr, length); +} + +int MessageIter::array_type() +{ + return dbus_message_iter_get_element_type((DBusMessageIter*)&_iter); +} + +int MessageIter::get_array( void* ptr ) +{ + int length; + dbus_message_iter_get_fixed_array((DBusMessageIter*)&_iter, ptr, &length); + return length; +} + +bool MessageIter::is_array() +{ + return dbus_message_iter_get_arg_type((DBusMessageIter*)&_iter) == DBUS_TYPE_ARRAY; +} + +bool MessageIter::is_dict() +{ + return is_array() && dbus_message_iter_get_element_type((DBusMessageIter*)_iter) == DBUS_TYPE_DICT_ENTRY; +} + +MessageIter MessageIter::new_array( const char* sig ) +{ + MessageIter arr(msg()); + dbus_message_iter_open_container( + (DBusMessageIter*)&_iter, DBUS_TYPE_ARRAY, sig, (DBusMessageIter*)&(arr._iter) + ); + return arr; +} + +MessageIter MessageIter::new_variant( const char* sig ) +{ + MessageIter var(msg()); + dbus_message_iter_open_container( + (DBusMessageIter*)_iter, DBUS_TYPE_VARIANT, sig, (DBusMessageIter*)&(var._iter) + ); + return var; +} + +MessageIter MessageIter::new_struct() +{ + MessageIter stu(msg()); + dbus_message_iter_open_container( + (DBusMessageIter*)_iter, DBUS_TYPE_STRUCT, NULL, (DBusMessageIter*)&(stu._iter) + ); + return stu; +} + +MessageIter MessageIter::new_dict_entry() +{ + MessageIter ent(msg()); + dbus_message_iter_open_container( + (DBusMessageIter*)_iter, DBUS_TYPE_DICT_ENTRY, NULL, (DBusMessageIter*)&(ent._iter) + ); + return ent; +} + +void MessageIter::close_container( MessageIter& container ) +{ + dbus_message_iter_close_container((DBusMessageIter*)&_iter, (DBusMessageIter*)&(container._iter)); +} + +static bool is_basic_type(int typecode) +{ + switch(typecode) + { + case 'y': + case 'b': + case 'n': + case 'q': + case 'i': + case 'u': + case 'x': + case 't': + case 'd': + case 's': + case 'o': + case 'g': + return true; + default: + return false; + } +} + +void MessageIter::copy_data( MessageIter& to ) +{ + for(MessageIter& from = *this; !from.at_end(); ++from) + { + if(is_basic_type(from.type())) + { + debug_log("copying basic type: %c", from.type()); + + unsigned char value[8]; + from.get_basic(from.type(), &value); + to.append_basic(from.type(), &value); + } + else + { + MessageIter from_container = from.recurse(); + char* sig = from_container.signature(); + + debug_log("copying compound type: %c[%s]", from.type(), sig); + + MessageIter to_container (to.msg()); + dbus_message_iter_open_container + ( + (DBusMessageIter*)&(to._iter), + from.type(), + from.type() == DBUS_TYPE_VARIANT ? NULL : sig, + (DBusMessageIter*)&(to_container._iter) + ); + + from_container.copy_data(to_container); + to.close_container(to_container); + free(sig); + } + } +} + +/* +*/ + +Message::Message() +: _pvt(new Private) +{ +} + +Message::Message( Message::Private* p, bool incref ) +: _pvt(p) +{ + if(_pvt->msg && incref) dbus_message_ref(_pvt->msg); +} + +Message::Message( const Message& m ) +: _pvt(m._pvt) +{ + dbus_message_ref(_pvt->msg); +} + +Message::~Message() +{ + dbus_message_unref(_pvt->msg); +} + +Message& Message::operator = ( const Message& m ) +{ + if(&m != this) + { + dbus_message_unref(_pvt->msg); + _pvt = m._pvt; + dbus_message_ref(_pvt->msg); + } + return *this; +} + +Message Message::copy() +{ + Private* pvt = new Private(dbus_message_copy(_pvt->msg)); + return Message(pvt); +} + +bool Message::append( int first_type, ... ) +{ + va_list vl; + va_start(vl, first_type); + + bool b = dbus_message_append_args_valist(_pvt->msg, first_type, vl); + + va_end(vl); + return b; +} + +void Message::terminate() +{ + dbus_message_append_args(_pvt->msg, DBUS_TYPE_INVALID); +} + +int Message::type() const +{ + return dbus_message_get_type(_pvt->msg); +} + +int Message::serial() const +{ + return dbus_message_get_serial(_pvt->msg); +} + +int Message::reply_serial() const +{ + return dbus_message_get_reply_serial(_pvt->msg); +} + +bool Message::reply_serial( int s ) +{ + return dbus_message_set_reply_serial(_pvt->msg, s); +} + +const char* Message::sender() const +{ + return dbus_message_get_sender(_pvt->msg); +} + +bool Message::sender( const char* s ) +{ + return dbus_message_set_sender(_pvt->msg, s); +} + +const char* Message::destination() const +{ + return dbus_message_get_destination(_pvt->msg); +} + +bool Message::destination( const char* s ) +{ + return dbus_message_set_destination(_pvt->msg, s); +} + +bool Message::is_error() const +{ + return type() == DBUS_MESSAGE_TYPE_ERROR; +} + +bool Message::is_signal( const char* interface, const char* member ) const +{ + return dbus_message_is_signal(_pvt->msg, interface, member); +} + +MessageIter Message::writer() +{ + MessageIter iter(*this); + dbus_message_iter_init_append(_pvt->msg, (DBusMessageIter*)&(iter._iter)); + return iter; +} + +MessageIter Message::reader() const +{ + MessageIter iter(const_cast(*this)); + dbus_message_iter_init(_pvt->msg, (DBusMessageIter*)&(iter._iter)); + return iter; +} + +/* +*/ + +ErrorMessage::ErrorMessage() +{ + _pvt->msg = dbus_message_new(DBUS_MESSAGE_TYPE_ERROR); +} + +ErrorMessage::ErrorMessage( const Message& to_reply, const char* name, const char* message ) +{ + _pvt->msg = dbus_message_new_error(to_reply._pvt->msg, name, message); +} + +bool ErrorMessage::operator == ( const ErrorMessage& m ) const +{ + return dbus_message_is_error(_pvt->msg, m.name()); +} + +const char* ErrorMessage::name() const +{ + return dbus_message_get_error_name(_pvt->msg); +} + +bool ErrorMessage::name( const char* n ) +{ + return dbus_message_set_error_name(_pvt->msg, n); +} + +/* +*/ + +SignalMessage::SignalMessage( const char* name ) +{ + _pvt->msg = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL); + member(name); +} + +SignalMessage::SignalMessage( const char* path, const char* interface, const char* name ) +{ + _pvt->msg = dbus_message_new_signal(path, interface, name); +} + +bool SignalMessage::operator == ( const SignalMessage& m ) const +{ + return dbus_message_is_signal(_pvt->msg, m.interface(), m.member()); +} + +const char* SignalMessage::interface() const +{ + return dbus_message_get_interface(_pvt->msg); +} + +bool SignalMessage::interface( const char* i ) +{ + return dbus_message_set_interface(_pvt->msg, i); +} + +const char* SignalMessage::member() const +{ + return dbus_message_get_member(_pvt->msg); +} + +bool SignalMessage::member( const char* m ) +{ + return dbus_message_set_member(_pvt->msg, m); +} + +const char* SignalMessage::path() const +{ + return dbus_message_get_path(_pvt->msg); +} + +char** SignalMessage::path_split() const +{ + char** p; + dbus_message_get_path_decomposed(_pvt->msg, &p); //todo: return as a std::vector ? + return p; +} + +bool SignalMessage::path( const char* p ) +{ + return dbus_message_set_path(_pvt->msg, p); +} + +/* +*/ + +CallMessage::CallMessage() +{ + _pvt->msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL); +} + +CallMessage::CallMessage( const char* dest, const char* path, const char* iface, const char* method ) +{ + _pvt->msg = dbus_message_new_method_call(dest, path, iface, method); +} + +bool CallMessage::operator == ( const CallMessage& m ) const +{ + return dbus_message_is_method_call(_pvt->msg, m.interface(), m.member()); +} + +const char* CallMessage::interface() const +{ + return dbus_message_get_interface(_pvt->msg); +} + +bool CallMessage::interface( const char* i ) +{ + return dbus_message_set_interface(_pvt->msg, i); +} + +const char* CallMessage::member() const +{ + return dbus_message_get_member(_pvt->msg); +} + +bool CallMessage::member( const char* m ) +{ + return dbus_message_set_member(_pvt->msg, m); +} + +const char* CallMessage::path() const +{ + return dbus_message_get_path(_pvt->msg); +} + +char** CallMessage::path_split() const +{ + char** p; + dbus_message_get_path_decomposed(_pvt->msg, &p); + return p; +} + +bool CallMessage::path( const char* p ) +{ + return dbus_message_set_path(_pvt->msg, p); +} + +const char* CallMessage::signature() const +{ + return dbus_message_get_signature(_pvt->msg); +} + +/* +*/ + +ReturnMessage::ReturnMessage( const CallMessage& callee ) +{ + _pvt = new Private(dbus_message_new_method_return(callee._pvt->msg)); +} + +const char* ReturnMessage::signature() const +{ + return dbus_message_get_signature(_pvt->msg); +} + Index: /branches/libffado-2.0/external/dbus/src/property.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/src/property.cpp (revision 562) +++ /branches/libffado-2.0/external/dbus/src/property.cpp (revision 562) @@ -0,0 +1,151 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +#include + +using namespace DBus; + +static const char* properties_name = "org.freedesktop.DBus.Properties"; + +PropertiesAdaptor::PropertiesAdaptor() +: InterfaceAdaptor(properties_name) +{ + register_method(PropertiesAdaptor, Get, Get); + register_method(PropertiesAdaptor, Set, Set); +} + +Message PropertiesAdaptor::Get( const CallMessage& call ) +{ + MessageIter ri = call.reader(); + + String iface_name; + String property_name; + + ri >> iface_name >> property_name; + + debug_log("requesting property %s on interface %s", property_name.c_str(), iface_name.c_str()); + + InterfaceAdaptor* interface = (InterfaceAdaptor*) find_interface(iface_name); + + if(!interface) + throw ErrorFailed("requested interface not found"); + + Variant* value = interface->get_property(property_name); + + if(!value) + throw ErrorFailed("requested property not found"); + + on_get_property(*interface, property_name, *value); + + ReturnMessage reply(call); + + MessageIter wi = reply.writer(); + + wi << *value; + return reply; +} + +Message PropertiesAdaptor::Set( const CallMessage& call ) +{ + MessageIter ri = call.reader(); + + String iface_name; + String property_name; + Variant value; + + ri >> iface_name >> property_name >> value; + + InterfaceAdaptor* interface = (InterfaceAdaptor*) find_interface(iface_name); + + if(!interface) + throw ErrorFailed("requested interface not found"); + + on_set_property(*interface, property_name, value); + + interface->set_property(property_name, value); + + ReturnMessage reply(call); + + return reply; +} + +IntrospectedInterface* const PropertiesAdaptor::introspect() const +{ + static IntrospectedArgument Get_args[] = + { + { "interface_name", "s", true }, + { "property_name", "s", true }, + { "value", "v", false }, + { 0, 0, 0 } + }; + static IntrospectedArgument Set_args[] = + { + { "interface_name", "s", true }, + { "property_name", "s", true }, + { "value", "v", true }, + { 0, 0, 0 } + }; + static IntrospectedMethod Properties_methods[] = + { + { "Get", Get_args }, + { "Set", Set_args }, + { 0, 0 } + }; + static IntrospectedMethod Properties_signals[] = + { + { 0, 0 } + }; + static IntrospectedProperty Properties_properties[] = + { + { 0, 0, 0, 0 } + }; + static IntrospectedInterface Properties_interface = + { + properties_name, + Properties_methods, + Properties_signals, + Properties_properties + }; + return &Properties_interface; +} + +PropertiesProxy::PropertiesProxy() +: InterfaceProxy(properties_name) +{ +} + +Variant PropertiesProxy::Get( const String& iface, const String& property ) +{ +//todo + Variant v; + return v; +} + +void PropertiesProxy::Set( const String& iface, const String& property, const Variant& value ) +{ +//todo +} + Index: /branches/libffado-2.0/external/dbus/src/dispatcher.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/src/dispatcher.cpp (revision 968) +++ /branches/libffado-2.0/external/dbus/src/dispatcher.cpp (revision 968) @@ -0,0 +1,241 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include + +#include + +#include "dispatcher_p.h" +#include "server_p.h" +#include "connection_p.h" + +DBus::Dispatcher* DBus::default_dispatcher = NULL; + +using namespace DBus; + +Timeout::Timeout( Timeout::Internal* i ) +: _int(i) +{ + dbus_timeout_set_data((DBusTimeout*)i, this, NULL); +} + +int Timeout::interval() const +{ + return dbus_timeout_get_interval((DBusTimeout*)_int); +} + +bool Timeout::enabled() const +{ + return dbus_timeout_get_enabled((DBusTimeout*)_int); +} + +bool Timeout::handle() +{ + return dbus_timeout_handle((DBusTimeout*)_int); +} + +/* +*/ + +Watch::Watch( Watch::Internal* i ) +: _int(i) +{ + dbus_watch_set_data((DBusWatch*)i, this, NULL); +} + +int Watch::descriptor() const +{ + return dbus_watch_get_fd((DBusWatch*)_int); +// return dbus_watch_get_unix_fd((DBusWatch*)_int); +} + +int Watch::flags() const +{ + return dbus_watch_get_flags((DBusWatch*)_int); +} + +bool Watch::enabled() const +{ + return dbus_watch_get_enabled((DBusWatch*)_int); +} + +bool Watch::handle( int flags ) +{ + return dbus_watch_handle((DBusWatch*)_int, flags); +} + +/* +*/ + +dbus_bool_t Dispatcher::Private::on_add_watch( DBusWatch* watch, void* data ) +{ + Dispatcher* d = static_cast(data); + + Watch::Internal* w = reinterpret_cast(watch); + + d->add_watch(w); + + return true; +} + +void Dispatcher::Private::on_rem_watch( DBusWatch* watch, void* data ) +{ + Dispatcher* d = static_cast(data); + + Watch* w = static_cast(dbus_watch_get_data(watch)); + + d->rem_watch(w); +} + +void Dispatcher::Private::on_toggle_watch( DBusWatch* watch, void* data ) +{ + Watch* w = static_cast(dbus_watch_get_data(watch)); + + w->toggle(); +} + +dbus_bool_t Dispatcher::Private::on_add_timeout( DBusTimeout* timeout, void* data ) +{ + Dispatcher* d = static_cast(data); + + Timeout::Internal* t = reinterpret_cast(timeout); + + d->add_timeout(t); + + return true; +} + +void Dispatcher::Private::on_rem_timeout( DBusTimeout* timeout, void* data ) +{ + Dispatcher* d = static_cast(data); + + Timeout* t = static_cast(dbus_timeout_get_data(timeout)); + + d->rem_timeout(t); +} + +void Dispatcher::Private::on_toggle_timeout( DBusTimeout* timeout, void* data ) +{ + Timeout* t = static_cast(dbus_timeout_get_data(timeout)); + + t->toggle(); +} + +void Dispatcher::queue_connection( Connection::Private* cp ) +{ + _pending_queue.push_back(cp); +} + +void Dispatcher::dispatch_pending() +{ + while(_pending_queue.size() > 0) + { + Connection::PrivatePList::iterator i, j; + + i = _pending_queue.begin(); + + while(i != _pending_queue.end()) + { + j = i; + + ++j; + + if((*i)->do_dispatch()) + _pending_queue.erase(i); + + i = j; + } + } +} + +#ifdef DBUS_HAS_THREADS_INIT_DEFAULT +void DBus::_init_threading() +{ + dbus_threads_init_default(); +} +#endif//DBUS_HAS_THREADS_INIT_DEFAULT + +void DBus::_init_threading( + MutexNewFn m1, + MutexFreeFn m2, + MutexLockFn m3, + MutexUnlockFn m4, + CondVarNewFn c1, + CondVarFreeFn c2, + CondVarWaitFn c3, + CondVarWaitTimeoutFn c4, + CondVarWakeOneFn c5, + CondVarWakeAllFn c6 +) +{ +#ifndef DBUS_HAS_RECURSIVE_MUTEX + DBusThreadFunctions functions = { + DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK | + DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK | + DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK | + DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK| + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK, + (DBusMutexNewFunction) m1, + (DBusMutexFreeFunction) m2, + (DBusMutexLockFunction) m3, + (DBusMutexUnlockFunction) m4, + (DBusCondVarNewFunction) c1, + (DBusCondVarFreeFunction) c2, + (DBusCondVarWaitFunction) c3, + (DBusCondVarWaitTimeoutFunction) c4, + (DBusCondVarWakeOneFunction) c5, + (DBusCondVarWakeAllFunction) c6 + }; +#else + DBusThreadFunctions functions = { + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK | + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK | + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK | + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK| + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK, + 0, 0, 0, 0, + (DBusCondVarNewFunction) c1, + (DBusCondVarFreeFunction) c2, + (DBusCondVarWaitFunction) c3, + (DBusCondVarWaitTimeoutFunction) c4, + (DBusCondVarWakeOneFunction) c5, + (DBusCondVarWakeAllFunction) c6, + (DBusRecursiveMutexNewFunction) m1, + (DBusRecursiveMutexFreeFunction) m2, + (DBusRecursiveMutexLockFunction) m3, + (DBusRecursiveMutexUnlockFunction) m4 + }; +#endif//DBUS_HAS_RECURSIVE_MUTEX + dbus_threads_init(&functions); +} Index: /branches/libffado-2.0/external/dbus/src/debug.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/src/debug.cpp (revision 562) +++ /branches/libffado-2.0/external/dbus/src/debug.cpp (revision 562) @@ -0,0 +1,55 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include + +#include +#include +#include + +static void _debug_log_default(const char* format, ...) +{ +#ifdef DEBUG + + static int debug_env = -1; + + if(debug_env < 0) debug_env = getenv("DBUSXX_VERBOSE") ? 1 : 0; + + if(debug_env) + { + va_list args; + va_start(args, format); + + fprintf(stderr, "dbus-c++: "); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + + va_end(args); + } + +#endif//DEBUG +} + +DBus::LogFunction DBus::debug_log = _debug_log_default; + Index: /branches/libffado-2.0/external/dbus/src/error.cpp =================================================================== --- /branches/libffado-2.0/external/dbus/src/error.cpp (revision 562) +++ /branches/libffado-2.0/external/dbus/src/error.cpp (revision 562) @@ -0,0 +1,86 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include +#include + +#include + +#include "message_p.h" +#include "internalerror.h" + +using namespace DBus; + +/* +*/ + +Error::Error() +: _int(new InternalError) +{} + +Error::Error(InternalError& i) +: _int(new InternalError(i)) +{} + +Error::Error( const char* name, const char* message ) +: _int(new InternalError) +{ + set(name, message); +} + +Error::Error( Message& m ) +: _int(new InternalError) +{ + dbus_set_error_from_message(&(_int->error), m._pvt->msg); +} + +Error::~Error() throw() +{ +} + +const char* Error::name() const +{ + return _int->error.name; +} + +const char* Error::message() const +{ + return _int->error.message; +} + +bool Error::is_set() const +{ + return *(_int); +} + +void Error::set( const char* name, const char* message ) +{ + dbus_set_error_const(&(_int->error), name, message); +} + +const char* Error::what() const throw() +{ + return _int->error.message; +} + Index: /branches/libffado-2.0/external/dbus/src/server_p.h =================================================================== --- /branches/libffado-2.0/external/dbus/src/server_p.h (revision 562) +++ /branches/libffado-2.0/external/dbus/src/server_p.h (revision 562) @@ -0,0 +1,57 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_SERVER_P_H +#define __DBUSXX_SERVER_P_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include + +namespace DBus { + +struct DXXAPILOCAL Server::Private +{ + DBusServer* server; + + Dispatcher* dispatcher; + + ConnectionList connections; + + Private( DBusServer* ); + + ~Private(); + + static void on_new_conn_cb( DBusServer* server, DBusConnection* conn, void* data ); +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_SERVER_P_H Index: /branches/libffado-2.0/external/dbus/src/internalerror.h =================================================================== --- /branches/libffado-2.0/external/dbus/src/internalerror.h (revision 562) +++ /branches/libffado-2.0/external/dbus/src/internalerror.h (revision 562) @@ -0,0 +1,77 @@ +/* + * + * D-Bus++ - C++ bindings for D-Bus + * + * Copyright (C) 2005-2007 Paolo Durante + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __DBUSXX_INTERNALERROR_H +#define __DBUSXX_INTERNALERROR_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +namespace DBus { + +struct DXXAPI InternalError +{ + DBusError error; + + InternalError() + { + dbus_error_init(&error); + } + + explicit InternalError( DBusError* e ) + { + dbus_error_init(&error); + dbus_move_error(e, &error); + } + + InternalError(const InternalError& ie) + { + dbus_error_init(&error); + dbus_move_error(const_cast(&(ie.error)), &error); + } + + ~InternalError() + { + dbus_error_free(&error); + } + + operator DBusError*() + { + return &error; + } + + operator bool() + { + return dbus_error_is_set(&error); + } +}; + +} /* namespace DBus */ + +#endif//__DBUSXX_INTERNALERROR_H Index: /branches/libffado-2.0/external/dbus/README =================================================================== --- /branches/libffado-2.0/external/dbus/README (revision 562) +++ /branches/libffado-2.0/external/dbus/README (revision 562) @@ -0,0 +1,2 @@ +DBUS C++ Bindings copied from: +http://dev.openwengo.org/svn/openwengo/wengophone-ng/branches/wengophone-dbus-api/libs/dbus Index: /branches/libffado-2.0/AUTHORS =================================================================== --- /branches/libffado-2.0/AUTHORS (revision 742) +++ /branches/libffado-2.0/AUTHORS (revision 742) @@ -0,0 +1,4 @@ +Daniel Wagner +Pieter Palmers +Jonathan Woithe +Arnold Krille Index: /branches/libffado-2.0/src/libieee1394/test-cyclecalc.cpp =================================================================== --- /branches/libffado-2.0/src/libieee1394/test-cyclecalc.cpp (revision 1047) +++ /branches/libffado-2.0/src/libieee1394/test-cyclecalc.cpp (revision 1047) @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "cycletimer.h" +#include "debugmodule/debugmodule.h" +#include + +DECLARE_GLOBAL_DEBUG_MODULE; + +int main() { + setDebugLevel(DEBUG_LEVEL_VERY_VERBOSE); + printf("Cycle timer operation tests (incomplete)\n"); + + /* TEST 1 + * check reconstruction of SYT RECEIVE timestamp + * + * The now_ctr has wrapped, while the cycle and syt have not + * + */ + + #ifdef DEBUG + uint32_t now_ctr = 0x140001DA; + uint64_t now = CYCLE_TIMER_TO_TICKS(0x140001DA); + unsigned int cycle = 7968; + uint16_t syt = 0x583B; + #endif + + debugOutput(DEBUG_LEVEL_VERBOSE,"NOW_CTR : %08X (%03us %04uc %04ut)\n", + now_ctr, + (unsigned int)CYCLE_TIMER_GET_SECS(now_ctr), + (unsigned int)CYCLE_TIMER_GET_CYCLES(now_ctr), + (unsigned int)CYCLE_TIMER_GET_OFFSET(now_ctr)); + + debugOutput(DEBUG_LEVEL_VERBOSE,"NOW : %011llu (%03us %04uc %04ut)\n", + now, + (unsigned int)TICKS_TO_SECS(now), + (unsigned int)TICKS_TO_CYCLES(now), + (unsigned int)TICKS_TO_OFFSET(now)); + debugOutput(DEBUG_LEVEL_VERBOSE,"SYT : %08X (%03us %04uc %04ut)\n", + syt, + (unsigned int)CYCLE_TIMER_GET_SECS(syt), + (unsigned int)CYCLE_TIMER_GET_CYCLES(syt), + (unsigned int)CYCLE_TIMER_GET_OFFSET(syt)); + debugOutput(DEBUG_LEVEL_VERBOSE,"CYCLE : %uc\n", + cycle); + #ifdef DEBUG + uint64_t calc_ts = sytRecvToFullTicks(syt, cycle, now_ctr); + #endif + + debugOutput(DEBUG_LEVEL_VERBOSE,"CALC_TS : %011llu (%03us %04uc %04ut)\n", + calc_ts, + (unsigned int)TICKS_TO_SECS(calc_ts), + (unsigned int)TICKS_TO_CYCLES(calc_ts), + (unsigned int)TICKS_TO_OFFSET(calc_ts)); + + +// BL: 1211722982: Debug (IsoHandler.cpp)[ 420] putPacket: received packet: length=168, channel=0, cycle=7968 +// BL: 1211723031: Debug (cycletimer.h)[ 308] sytRecvToFullTicks: SYT=583B CY=7968 CTR=140001DA +// BL: 1211723037: Debug (StreamProcessor.cpp)[ 346] putPacket: RECV: CY=7968 TS=00245679163 +// BL: 1211723043: Debug (AmdtpReceiveStreamProcessor.cpp)[ 135] processPacketData: STMP: 245679163ticks | syt_interval=8, tpf=557.254395 +// BL: 1211723051: Debug (TimestampedBuffer.cpp)[1153] incrementFrameCounter: nbframes: 8, m_update_period: 8 +// BL: 1211723052: Debug (AmdtpTransmitStreamProcessor.cpp)[ 250] generatePacketHeader: Too early: CY=0254, TC=0257, CUT=0003, TST=00271126073 (0257), TSP=00271137849 (0261) +// BL: 1211723055: Debug (TimestampedBuffer.cpp)[1155] incrementFrameCounter: tail TS: 270250705.174, next tail TS: 270255163.209 +// BL: 1211723062: Debug (TimestampedBuffer.cpp)[1157] incrementFrameCounter: new TS: 245679163.000, wrapped new TS: 245679163.000 +// + +} Index: /branches/libffado-2.0/src/libieee1394/CycleTimerHelper.cpp =================================================================== --- /branches/libffado-2.0/src/libieee1394/CycleTimerHelper.cpp (revision 1184) +++ /branches/libffado-2.0/src/libieee1394/CycleTimerHelper.cpp (revision 1184) @@ -0,0 +1,757 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "config.h" + +#include "CycleTimerHelper.h" +#include "ieee1394service.h" +#include "libutil/PosixThread.h" +#include "libutil/PosixMutex.h" +#include "libutil/Atomic.h" +#include "libutil/Watchdog.h" + +#define DLL_PI (3.141592653589793238) +#define DLL_SQRT2 (1.414213562373095049) + +// the high-bandwidth coefficients are used +// to speed up inital tracking +#define DLL_BANDWIDTH_HIGH (0.1) +#define DLL_OMEGA_HIGH (2.0*DLL_PI*DLL_BANDWIDTH_HIGH) +#define DLL_COEFF_B_HIGH (DLL_SQRT2 * DLL_OMEGA_HIGH) +#define DLL_COEFF_C_HIGH (DLL_OMEGA_HIGH * DLL_OMEGA_HIGH) + +// the low-bandwidth coefficients are used once we have a +// good estimate of the internal parameters +#define DLL_BANDWIDTH (0.1) +#define DLL_OMEGA (2.0*DLL_PI*DLL_BANDWIDTH) +#define DLL_COEFF_B (DLL_SQRT2 * DLL_OMEGA) +#define DLL_COEFF_C (DLL_OMEGA * DLL_OMEGA) + +// is 1 sec +#define UPDATES_WITH_HIGH_BANDWIDTH \ + (1000000 / IEEE1394SERVICE_CYCLETIMER_DLL_UPDATE_INTERVAL_USEC) + +IMPL_DEBUG_MODULE( CycleTimerHelper, CycleTimerHelper, DEBUG_LEVEL_NORMAL ); + +CycleTimerHelper::CycleTimerHelper(Ieee1394Service &parent, unsigned int update_period_us) + : m_Parent ( parent ) + , m_ticks_per_update ( ((uint64_t)TICKS_PER_SECOND) * ((uint64_t)update_period_us) / 1000000ULL ) + , m_usecs_per_update ( update_period_us ) + , m_avg_wakeup_delay ( 0.0 ) + , m_dll_e2 ( 0.0 ) + , m_current_time_usecs ( 0 ) + , m_next_time_usecs ( 0 ) + , m_current_time_ticks ( 0 ) + , m_next_time_ticks ( 0 ) + , m_first_run ( true ) + , m_sleep_until ( 0 ) + , m_cycle_timer_prev ( 0 ) + , m_cycle_timer_ticks_prev ( 0 ) + , m_high_bw_updates ( UPDATES_WITH_HIGH_BANDWIDTH ) + , m_current_shadow_idx ( 0 ) + , m_Thread ( NULL ) + , m_realtime ( false ) + , m_priority ( 0 ) + , m_update_lock( new Util::PosixMutex() ) + , m_busreset_functor ( NULL) + , m_unhandled_busreset ( false ) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Create %p...\n", this); +} + +CycleTimerHelper::CycleTimerHelper(Ieee1394Service &parent, unsigned int update_period_us, bool rt, int prio) + : m_Parent ( parent ) + , m_ticks_per_update ( ((uint64_t)TICKS_PER_SECOND) * ((uint64_t)update_period_us) / 1000000ULL ) + , m_usecs_per_update ( update_period_us ) + , m_avg_wakeup_delay ( 0.0 ) + , m_dll_e2 ( 0.0 ) + , m_current_time_usecs ( 0 ) + , m_next_time_usecs ( 0 ) + , m_current_time_ticks ( 0 ) + , m_next_time_ticks ( 0 ) + , m_first_run ( true ) + , m_sleep_until ( 0 ) + , m_cycle_timer_prev ( 0 ) + , m_cycle_timer_ticks_prev ( 0 ) + , m_high_bw_updates ( UPDATES_WITH_HIGH_BANDWIDTH ) + , m_current_shadow_idx ( 0 ) + , m_Thread ( NULL ) + , m_realtime ( rt ) + , m_priority ( prio ) + , m_update_lock( new Util::PosixMutex() ) + , m_busreset_functor ( NULL) + , m_unhandled_busreset ( false ) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Create %p...\n", this); +} + +CycleTimerHelper::~CycleTimerHelper() +{ + if (m_Thread) { + m_Thread->Stop(); + delete m_Thread; + } + + // unregister the bus reset handler + if(m_busreset_functor) { + m_Parent.remBusResetHandler( m_busreset_functor ); + delete m_busreset_functor; + } + delete m_update_lock; +} + +bool +CycleTimerHelper::Start() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Start %p...\n", this); + + if(!initValues()) { + debugFatal("(%p) Could not init values\n", this); + return false; + } + + m_Thread = new Util::PosixThread(this, m_realtime, m_priority, + PTHREAD_CANCEL_DEFERRED); + if(!m_Thread) { + debugFatal("No thread\n"); + return false; + } + // register the thread with the RT watchdog + Util::Watchdog *watchdog = m_Parent.getWatchdog(); + if(watchdog) { + if(!watchdog->registerThread(m_Thread)) { + debugWarning("could not register update thread with watchdog\n"); + } + } else { + debugWarning("could not find valid watchdog\n"); + } + + if (m_Thread->Start() != 0) { + debugFatal("Could not start update thread\n"); + return false; + } + return true; +} + +bool +CycleTimerHelper::initValues() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) Init values...\n", this ); + Util::MutexLockHelper lock(*m_update_lock); + + // initialize the 'prev ctr' values + uint64_t local_time; + int maxtries2 = 10; + do { + debugOutput( DEBUG_LEVEL_VERBOSE, "Read CTR...\n" ); + if(!m_Parent.readCycleTimerReg(&m_cycle_timer_prev, &local_time)) { + debugError("Could not read cycle timer register\n"); + return false; + } + if (m_cycle_timer_prev == 0) { + debugOutput(DEBUG_LEVEL_VERBOSE, + "Bogus CTR: %08X on try %02d\n", + m_cycle_timer_prev, maxtries2); + } + debugOutput( DEBUG_LEVEL_VERBOSE, " read : CTR: %11lu, local: %17llu\n", + m_cycle_timer_prev, local_time); + debugOutput(DEBUG_LEVEL_VERBOSE, + " ctr : 0x%08X %11llu (%03us %04ucy %04uticks)\n", + (uint32_t)m_cycle_timer_prev, (uint64_t)CYCLE_TIMER_TO_TICKS(m_cycle_timer_prev), + (unsigned int)CYCLE_TIMER_GET_SECS( m_cycle_timer_prev ), + (unsigned int)CYCLE_TIMER_GET_CYCLES( m_cycle_timer_prev ), + (unsigned int)CYCLE_TIMER_GET_OFFSET( m_cycle_timer_prev ) ); + + } while (m_cycle_timer_prev == 0 && maxtries2--); + m_cycle_timer_ticks_prev = CYCLE_TIMER_TO_TICKS(m_cycle_timer_prev); + +#if IEEE1394SERVICE_USE_CYCLETIMER_DLL + debugOutput( DEBUG_LEVEL_VERBOSE, "requesting DLL re-init...\n" ); + Util::SystemTimeSource::SleepUsecRelative(1000); // some time to settle + m_high_bw_updates = UPDATES_WITH_HIGH_BANDWIDTH; + if(!initDLL()) { + debugError("(%p) Could not init DLL\n", this); + return false; + } + // make the DLL re-init itself as if it were started up + m_first_run = true; +#endif + debugOutput( DEBUG_LEVEL_VERBOSE, "ready...\n" ); + return true; +} + +bool +CycleTimerHelper::Init() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Initialize %p...\n", this); + + // register a bus reset handler + m_busreset_functor = new Util::MemberFunctor0< CycleTimerHelper*, + void (CycleTimerHelper::*)() > + ( this, &CycleTimerHelper::busresetHandler, false ); + if ( !m_busreset_functor ) { + debugFatal( "(%p) Could not create busreset handler\n", this ); + return false; + } + m_Parent.addBusResetHandler( m_busreset_functor ); + + #ifdef DEBUG + m_last_loop_entry = 0; + m_successive_short_loops = 0; + #endif + + return true; +} + +void +CycleTimerHelper::busresetHandler() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Bus reset...\n" ); + m_unhandled_busreset = true; + // whenever a bus reset occurs, the root node can change, + // and the CTR timer can be reset. We should hence reinit + // the DLL + if(!initValues()) { + debugError("(%p) Could not re-init values\n", this); + } + m_unhandled_busreset = false; +} + +bool +CycleTimerHelper::setThreadParameters(bool rt, int priority) { + debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) switch to: (rt=%d, prio=%d)...\n", this, rt, priority); + if (priority > THREAD_MAX_RTPRIO) priority = THREAD_MAX_RTPRIO; // cap the priority + m_realtime = rt; + m_priority = priority; + +#if IEEE1394SERVICE_USE_CYCLETIMER_DLL + if (m_Thread) { + if (m_realtime) { + m_Thread->AcquireRealTime(m_priority); + } else { + m_Thread->DropRealTime(); + } + } +#endif + + return true; +} + +#if IEEE1394SERVICE_USE_CYCLETIMER_DLL +float +CycleTimerHelper::getRate() +{ + float rate = (float)(diffTicks((uint64_t)m_next_time_ticks, (uint64_t)m_current_time_ticks)); + rate /= (float)(m_next_time_usecs - m_current_time_usecs); + return rate; +} + +float +CycleTimerHelper::getNominalRate() +{ + float rate = ((double)TICKS_PER_SECOND) / 1000000.0; + return rate; +} + +/* + * call with lock held + */ +bool +CycleTimerHelper::initDLL() { + uint32_t cycle_timer; + uint64_t local_time; + uint64_t cycle_timer_ticks; + + if(!readCycleTimerWithRetry(&cycle_timer, &local_time, 10)) { + debugError("Could not read cycle timer register\n"); + return false; + } + cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(cycle_timer); + + debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, " read : CTR: %11lu, local: %17llu\n", + cycle_timer, local_time); + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + " ctr : 0x%08X %11llu (%03us %04ucy %04uticks)\n", + (uint32_t)cycle_timer, (uint64_t)cycle_timer_ticks, + (unsigned int)TICKS_TO_SECS( (uint64_t)cycle_timer_ticks ), + (unsigned int)TICKS_TO_CYCLES( (uint64_t)cycle_timer_ticks ), + (unsigned int)TICKS_TO_OFFSET( (uint64_t)cycle_timer_ticks ) ); + + m_sleep_until = local_time + m_usecs_per_update; + m_dll_e2 = m_ticks_per_update; + m_current_time_usecs = local_time; + m_next_time_usecs = m_current_time_usecs + m_usecs_per_update; + m_current_time_ticks = CYCLE_TIMER_TO_TICKS( cycle_timer ); + m_next_time_ticks = addTicks( (uint64_t)m_current_time_ticks, (uint64_t)m_dll_e2); + debugOutput(DEBUG_LEVEL_VERBOSE, " (%p) First run\n", this); + debugOutput(DEBUG_LEVEL_VERBOSE, + " usecs/update: %lu, ticks/update: %lu, m_dll_e2: %f\n", + m_usecs_per_update, m_ticks_per_update, m_dll_e2); + debugOutput(DEBUG_LEVEL_VERBOSE, + " usecs current: %f, next: %f\n", + m_current_time_usecs, m_next_time_usecs); + debugOutput(DEBUG_LEVEL_VERBOSE, + " ticks current: %f, next: %f\n", + m_current_time_ticks, m_next_time_ticks); + return true; +} + +bool +CycleTimerHelper::Execute() +{ + debugOutput( DEBUG_LEVEL_ULTRA_VERBOSE, "Execute %p...\n", this); + + #ifdef DEBUG + uint64_t now = m_Parent.getCurrentTimeAsUsecs(); + int diff = now - m_last_loop_entry; + if(diff < 100) { + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "(%p) short loop detected (%d usec), cnt: %d\n", + this, diff, m_successive_short_loops); + m_successive_short_loops++; + if(m_successive_short_loops > 100) { + debugError("Shutting down runaway thread\n"); + return false; + } + } else { + // reset the counter + m_successive_short_loops = 0; + } + m_last_loop_entry = now; + #endif + + if (!m_first_run) { + // wait for the next update period + //#if DEBUG_EXTREME_ENABLE + #ifdef DEBUG + ffado_microsecs_t now = Util::SystemTimeSource::getCurrentTimeAsUsecs(); + int sleep_time = m_sleep_until - now; + debugOutput( DEBUG_LEVEL_ULTRA_VERBOSE, "(%p) Sleep until %lld/%f (now: %lld, diff=%d) ...\n", + this, m_sleep_until, m_next_time_usecs, now, sleep_time); + #endif + Util::SystemTimeSource::SleepUsecAbsolute(m_sleep_until); + debugOutput( DEBUG_LEVEL_ULTRA_VERBOSE, " (%p) back...\n", this); + } + + uint32_t cycle_timer; + uint64_t local_time; + int64_t usecs_late; + int ntries=4; + uint64_t cycle_timer_ticks; + double diff_ticks; + + // if the difference between the predicted value and the + // actual value seems to be too large, retry reading the cycle timer + // some host controllers return bogus values on some reads + // (looks like a non-atomic update of the register) + do { + debugOutput( DEBUG_LEVEL_ULTRA_VERBOSE, "(%p) reading cycle timer register...\n", this); + if(!readCycleTimerWithRetry(&cycle_timer, &local_time, 10)) { + debugError("Could not read cycle timer register\n"); + return false; + } + usecs_late = local_time - m_sleep_until; + cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(cycle_timer); + diff_ticks = diffTicks(cycle_timer_ticks, (int64_t)m_next_time_ticks); + + // check for unrealistic CTR reads (NEC controller does that sometimes) + if(diff_ticks < -((double)TICKS_PER_HALFCYCLE)) { + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, + "(%p) have to retry CTR read, diff unrealistic: diff: %f, max: %f (try: %d)\n", + this, diff_ticks, -((double)TICKS_PER_HALFCYCLE), ntries); + } + + } while( diff_ticks < -((double)TICKS_PER_HALFCYCLE) + && --ntries && !m_first_run && !m_unhandled_busreset); + + // grab the lock after sleeping, otherwise we can't be interrupted by + // the busreset thread (lower prio) + // also grab it after reading the CTR register such that the jitter between + // wakeup and read is as small as possible + Util::MutexLockHelper lock(*m_update_lock); + + // // simulate a random scheduling delay between (0-10ms) + // ffado_microsecs_t tmp = Util::SystemTimeSource::SleepUsecRandom(10000); + // debugOutput( DEBUG_LEVEL_VERBOSE, " (%p) random sleep of %llu usecs...\n", this, tmp); + + if(m_unhandled_busreset) { + debugOutput(DEBUG_LEVEL_VERBOSE, + "(%p) Skipping DLL update due to unhandled busreset\n", this); + m_sleep_until += m_usecs_per_update; + // keep the thread running + return true; + } + + debugOutputExtreme( DEBUG_LEVEL_ULTRA_VERBOSE, " read : CTR: %11lu, local: %17llu\n", + cycle_timer, local_time); + debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, + " ctr : 0x%08X %11llu (%03us %04ucy %04uticks)\n", + (uint32_t)cycle_timer, (uint64_t)cycle_timer_ticks, + (unsigned int)TICKS_TO_SECS( (uint64_t)cycle_timer_ticks ), + (unsigned int)TICKS_TO_CYCLES( (uint64_t)cycle_timer_ticks ), + (unsigned int)TICKS_TO_OFFSET( (uint64_t)cycle_timer_ticks ) ); + + if (m_first_run) { + if(!initDLL()) { + debugError("(%p) Could not init DLL\n", this); + return false; + } + m_first_run = false; + } else { + // calculate next sleep time + m_sleep_until += m_usecs_per_update; + + // correct for the latency between the wakeup and the actual CTR + // read. The only time we can trust is the time returned by the + // CTR read kernel call, since that (should be) atomically read + // together with the ctr register itself. + + // if we are usecs_late usecs late + // the cycle timer has ticked approx ticks_late ticks too much + // if we are woken up early (which shouldn't happen according to POSIX) + // the cycle timer has ticked approx -ticks_late too little + int64_t ticks_late = (usecs_late * TICKS_PER_SECOND) / 1000000LL; + // the corrected difference between predicted and actual ctr + // i.e. DLL error signal + double diff_ticks_corr; + if (ticks_late > 0) { + diff_ticks_corr = diff_ticks - ticks_late; + debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, + "diff_ticks_corr=%f, diff_ticks = %f, ticks_late = %lld\n", + diff_ticks_corr, diff_ticks, ticks_late); + } else { + debugError("Early wakeup, should not happen!\n"); + // recover + diff_ticks_corr = diff_ticks + ticks_late; + } + + #ifdef DEBUG + // makes no sense if not running realtime + if(m_realtime && usecs_late > 1000) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Rather late wakeup: %lld usecs\n", usecs_late); + } + #endif + + // update the x-axis values + m_current_time_ticks = m_next_time_ticks; + + // decide what coefficients to use + double coeff_b, coeff_c; + if (m_high_bw_updates > 0) { + coeff_b = DLL_COEFF_B_HIGH; + coeff_c = DLL_COEFF_C_HIGH; + m_high_bw_updates--; + if (m_high_bw_updates == 0) { + debugOutput(DEBUG_LEVEL_VERBOSE, + "Switching to low-bandwidth coefficients\n"); + } + } else { + coeff_b = DLL_COEFF_B; + coeff_c = DLL_COEFF_C; + } + + // it should be ok to not do this in tick space + // since diff_ticks_corr should not be near wrapping + // (otherwise we are out of range. we need a few calls + // w/o wrapping for this to work. That should not be + // an issue as long as the update interval is smaller + // than the wrapping interval.) + // and coeff_b < 1, hence tmp is not near wrapping + + double step_ticks = (coeff_b * diff_ticks_corr); + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "diff_ticks_corr=%f, step_ticks=%f\n", + diff_ticks_corr, step_ticks); + + // the same goes for m_dll_e2, which should be approx equal + // to the ticks/usec rate (= 24.576) hence also not near + // wrapping + step_ticks += m_dll_e2; + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "add %f ticks to step_ticks => step_ticks=%f\n", + m_dll_e2, step_ticks); + + // it can't be that we have to update to a value in the past + if(step_ticks < 0) { + debugError("negative step: %f! (correcting to nominal)\n", step_ticks); + // recover to an estimated value + step_ticks = (double)m_ticks_per_update; + } + + if(step_ticks > TICKS_PER_SECOND) { + debugWarning("rather large step: %f ticks (> 1sec)\n", step_ticks); + } + + // now add the step ticks with wrapping. + m_next_time_ticks = (double)(addTicks((uint64_t)m_current_time_ticks, (uint64_t)step_ticks)); + + // update the DLL state + m_dll_e2 += coeff_c * diff_ticks_corr; + + // update the y-axis values + m_current_time_usecs = m_next_time_usecs; + m_next_time_usecs += m_usecs_per_update; + + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + " usecs: current: %f next: %f usecs_late=%lld ticks_late=%lld\n", + m_current_time_usecs, m_next_time_usecs, usecs_late, ticks_late); + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + " ticks: current: %f next: %f diff=%f\n", + m_current_time_ticks, m_next_time_ticks, diff_ticks); + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + " ticks: current: %011llu (%03us %04ucy %04uticks)\n", + (uint64_t)m_current_time_ticks, + (unsigned int)TICKS_TO_SECS( (uint64_t)m_current_time_ticks ), + (unsigned int)TICKS_TO_CYCLES( (uint64_t)m_current_time_ticks ), + (unsigned int)TICKS_TO_OFFSET( (uint64_t)m_current_time_ticks ) ); + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + " ticks: next : %011llu (%03us %04ucy %04uticks)\n", + (uint64_t)m_next_time_ticks, + (unsigned int)TICKS_TO_SECS( (uint64_t)m_next_time_ticks ), + (unsigned int)TICKS_TO_CYCLES( (uint64_t)m_next_time_ticks ), + (unsigned int)TICKS_TO_OFFSET( (uint64_t)m_next_time_ticks ) ); + + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + " state: local: %11llu, dll_e2: %f, rate: %f\n", + local_time, m_dll_e2, getRate()); + } + + // prepare the new compute vars + struct compute_vars new_vars; + new_vars.ticks = (uint64_t)(m_current_time_ticks); + new_vars.usecs = (uint64_t)m_current_time_usecs; + new_vars.rate = getRate(); + + // get the next index + unsigned int next_idx = (m_current_shadow_idx + 1) % CTRHELPER_NB_SHADOW_VARS; + + // update the next index position + m_shadow_vars[next_idx] = new_vars; + + // then we can update the current index + m_current_shadow_idx = next_idx; + +#ifdef DEBUG + // do some verification + // we re-read a valid ctr timestamp + // then we use the attached system time to calculate + // the DLL generated timestamp and we check what the + // difference is + + if(!readCycleTimerWithRetry(&cycle_timer, &local_time, 10)) { + debugError("Could not read cycle timer register (verify)\n"); + return true; // true since this is a check only + } + cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(cycle_timer); + + // only check when successful + int64_t time_diff = local_time - new_vars.usecs; + double y_step_in_ticks = ((double)time_diff) * new_vars.rate; + int64_t y_step_in_ticks_int = (int64_t)y_step_in_ticks; + uint64_t offset_in_ticks_int = new_vars.ticks; + uint32_t dll_time; + if (y_step_in_ticks_int > 0) { + dll_time = addTicks(offset_in_ticks_int, y_step_in_ticks_int); + } else { + dll_time = substractTicks(offset_in_ticks_int, -y_step_in_ticks_int); + } + int32_t ctr_diff = cycle_timer_ticks-dll_time; + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "(%p) CTR DIFF: HW %010llu - DLL %010lu = %010ld (%s)\n", + this, cycle_timer_ticks, dll_time, ctr_diff, (ctr_diff>0?"lag":"lead")); +#endif + + return true; +} + +uint32_t +CycleTimerHelper::getCycleTimerTicks() +{ + uint64_t now = m_Parent.getCurrentTimeAsUsecs(); + return getCycleTimerTicks(now); +} + +uint32_t +CycleTimerHelper::getCycleTimerTicks(uint64_t now) +{ + uint32_t retval; + struct compute_vars *my_vars; + + // get pointer and copy the contents + // no locking should be needed since we have more than one + // of these vars available, and our use will always be finished before + // m_current_shadow_idx changes since this thread's priority should + // be higher than the one of the writer thread. Even if not, we only have to ensure + // that the used dataset is consistent. We can use an older dataset if it's consistent + // since it will also provide a fairly decent extrapolation. + my_vars = m_shadow_vars + m_current_shadow_idx; + + int64_t time_diff = now - my_vars->usecs; + double y_step_in_ticks = ((double)time_diff) * my_vars->rate; + int64_t y_step_in_ticks_int = (int64_t)y_step_in_ticks; + uint64_t offset_in_ticks_int = my_vars->ticks; + + if (y_step_in_ticks_int > 0) { + retval = addTicks(offset_in_ticks_int, y_step_in_ticks_int); +/* debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "y_step_in_ticks_int > 0: %lld, time_diff: %f, rate: %f, retval: %lu\n", + y_step_in_ticks_int, time_diff, my_vars.rate, retval);*/ + } else { + retval = substractTicks(offset_in_ticks_int, -y_step_in_ticks_int); + + // this can happen if the update thread was woken up earlier than it should have been +/* debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "y_step_in_ticks_int <= 0: %lld, time_diff: %f, rate: %f, retval: %lu\n", + y_step_in_ticks_int, time_diff, my_vars.rate, retval);*/ + } + + return retval; +} + +uint32_t +CycleTimerHelper::getCycleTimer() +{ + uint64_t now = m_Parent.getCurrentTimeAsUsecs(); + return getCycleTimer(now); +} + +uint32_t +CycleTimerHelper::getCycleTimer(uint64_t now) +{ + uint32_t ticks = getCycleTimerTicks(now); + uint32_t result = TICKS_TO_CYCLE_TIMER(ticks); +#ifdef DEBUG + if(CYCLE_TIMER_TO_TICKS(result) != ticks) { + debugWarning("Bad ctr conversion"); + } +#endif + return result; +} + +#else + +float +CycleTimerHelper::getRate() +{ + return getNominalRate(); +} + +float +CycleTimerHelper::getNominalRate() +{ + float rate = ((double)TICKS_PER_SECOND) / 1000000.0; + return rate; +} + +bool +CycleTimerHelper::Execute() +{ + usleep(1000*1000); + return true; +} + +uint32_t +CycleTimerHelper::getCycleTimerTicks() +{ + return CYCLE_TIMER_TO_TICKS(getCycleTimer()); +} + +uint32_t +CycleTimerHelper::getCycleTimerTicks(uint64_t now) +{ + debugWarning("not implemented!\n"); + return getCycleTimerTicks(); +} + +uint32_t +CycleTimerHelper::getCycleTimer() +{ + uint32_t cycle_timer; + uint64_t local_time; + readCycleTimerWithRetry(&cycle_timer, &local_time, 10); + return cycle_timer; +} + +uint32_t +CycleTimerHelper::getCycleTimer(uint64_t now) +{ + debugWarning("not implemented!\n"); + return getCycleTimer(); +} + +#endif + +bool +CycleTimerHelper::readCycleTimerWithRetry(uint32_t *cycle_timer, uint64_t *local_time, int ntries) +{ + bool good=false; + int maxtries = ntries; + + do { + // the ctr read can return 0 sometimes. if that happens, reread the ctr. + int maxtries2=ntries; + do { + if(!m_Parent.readCycleTimerReg(cycle_timer, local_time)) { + debugError("Could not read cycle timer register\n"); + return false; + } + if (*cycle_timer == 0) { + debugOutput(DEBUG_LEVEL_VERBOSE, + "Bogus CTR: %08X on try %02d\n", + *cycle_timer, maxtries2); + } + } while (*cycle_timer == 0 && maxtries2--); + + // catch bogus ctr reads (can happen) + uint64_t cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(*cycle_timer); + + if (diffTicks(cycle_timer_ticks, m_cycle_timer_ticks_prev) < 0) { + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, + "non-monotonic CTR (try %02d): %llu -> %llu\n", + maxtries, m_cycle_timer_ticks_prev, cycle_timer_ticks); + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, + " : %08X -> %08X\n", + m_cycle_timer_prev, *cycle_timer); + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, + " current: %011llu (%03us %04ucy %04uticks)\n", + cycle_timer_ticks, + (unsigned int)TICKS_TO_SECS( cycle_timer_ticks ), + (unsigned int)TICKS_TO_CYCLES( cycle_timer_ticks ), + (unsigned int)TICKS_TO_OFFSET( cycle_timer_ticks ) ); + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, + " prev : %011llu (%03us %04ucy %04uticks)\n", + m_cycle_timer_ticks_prev, + (unsigned int)TICKS_TO_SECS( m_cycle_timer_ticks_prev ), + (unsigned int)TICKS_TO_CYCLES( m_cycle_timer_ticks_prev ), + (unsigned int)TICKS_TO_OFFSET( m_cycle_timer_ticks_prev ) ); + } else { + good = true; + m_cycle_timer_prev = *cycle_timer; + m_cycle_timer_ticks_prev = cycle_timer_ticks; + } + } while (!good && maxtries--); + return true; +} + +void +CycleTimerHelper::setVerboseLevel(int l) +{ + setDebugLevel(l); +} Index: /branches/libffado-2.0/src/libieee1394/csr1212.h =================================================================== --- /branches/libffado-2.0/src/libieee1394/csr1212.h (revision 864) +++ /branches/libffado-2.0/src/libieee1394/csr1212.h (revision 864) @@ -0,0 +1,754 @@ +/* + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ +/* + * csr1212.h -- IEEE 1212 Control and Status Register support for Linux + * + * Copyright (C) 2003 Francois Retief + * Steve Kinneberg + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __CSR1212_H__ +#define __CSR1212_H__ + +#ifdef __cplusplus + extern "C" { +#endif + +/* Compatibility layer */ +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include +#include + +#define CSR1212_MALLOC(size) vmalloc((size)) +#define CSR1212_FREE(ptr) vfree(ptr) +#define CSR1212_BE16_TO_CPU(quad) be16_to_cpu(quad) +#define CSR1212_CPU_TO_BE16(quad) cpu_to_be16(quad) +#define CSR1212_BE32_TO_CPU(quad) be32_to_cpu(quad) +#define CSR1212_CPU_TO_BE32(quad) cpu_to_be32(quad) +#define CSR1212_BE64_TO_CPU(quad) be64_to_cpu(quad) +#define CSR1212_CPU_TO_BE64(quad) cpu_to_be64(quad) + +#define CSR1212_LE16_TO_CPU(quad) le16_to_cpu(quad) +#define CSR1212_CPU_TO_LE16(quad) cpu_to_le16(quad) +#define CSR1212_LE32_TO_CPU(quad) le32_to_cpu(quad) +#define CSR1212_CPU_TO_LE32(quad) cpu_to_le32(quad) +#define CSR1212_LE64_TO_CPU(quad) le64_to_cpu(quad) +#define CSR1212_CPU_TO_LE64(quad) cpu_to_le64(quad) + +#include +#define CSR1212_SUCCESS (0) +#define CSR1212_EINVAL (-EINVAL) +#define CSR1212_ENOMEM (-ENOMEM) +#define CSR1212_ENOENT (-ENOENT) +#define CSR1212_EIO (-EIO) +#define CSR1212_EBUSY (-EBUSY) + +#else /* Userspace */ + +#include +#include +#define CSR1212_MALLOC(size) calloc(1,size) +#define CSR1212_FREE(ptr) free(ptr) +#include +#if __BYTE_ORDER == __LITTLE_ENDIAN +#include +#define CSR1212_BE16_TO_CPU(quad) bswap_16(quad) +#define CSR1212_CPU_TO_BE16(quad) bswap_16(quad) +#define CSR1212_BE32_TO_CPU(quad) bswap_32(quad) +#define CSR1212_CPU_TO_BE32(quad) bswap_32(quad) +#define CSR1212_BE64_TO_CPU(quad) bswap_64(quad) +#define CSR1212_CPU_TO_BE64(quad) bswap_64(quad) + +#define CSR1212_LE16_TO_CPU(quad) (quad) +#define CSR1212_CPU_TO_LE16(quad) (quad) +#define CSR1212_LE32_TO_CPU(quad) (quad) +#define CSR1212_CPU_TO_LE32(quad) (quad) +#define CSR1212_LE64_TO_CPU(quad) (quad) +#define CSR1212_CPU_TO_LE64(quad) (quad) +#else +#define CSR1212_BE16_TO_CPU(quad) (quad) +#define CSR1212_CPU_TO_BE16(quad) (quad) +#define CSR1212_BE32_TO_CPU(quad) (quad) +#define CSR1212_CPU_TO_BE32(quad) (quad) +#define CSR1212_BE64_TO_CPU(quad) (quad) +#define CSR1212_CPU_TO_BE64(quad) (quad) + +#define CSR1212_LE16_TO_CPU(quad) bswap_16(quad) +#define CSR1212_CPU_TO_LE16(quad) bswap_16(quad) +#define CSR1212_LE32_TO_CPU(quad) bswap_32(quad) +#define CSR1212_CPU_TO_LE32(quad) bswap_32(quad) +#define CSR1212_LE64_TO_CPU(quad) bswap_64(quad) +#define CSR1212_CPU_TO_LE64(quad) bswap_64(quad) +#endif + +#include +#define CSR1212_SUCCESS (0) +#define CSR1212_EINVAL (EINVAL) +#define CSR1212_ENOMEM (ENOMEM) +#define CSR1212_ENOENT (ENOENT) +#define CSR1212_EIO (EIO) +#define CSR1212_EBUSY (EBUSY) + +#endif + + +#define CSR1212_KV_VAL_MASK 0xffffff +#define CSR1212_KV_KEY_SHIFT 24 +#define CSR1212_KV_KEY_TYPE_SHIFT 6 +#define CSR1212_KV_KEY_ID_MASK 0x3f +#define CSR1212_KV_KEY_TYPE_MASK 0x3 /* After shift */ + + +/* CSR 1212 key types */ +#define CSR1212_KV_TYPE_IMMEDIATE 0 +#define CSR1212_KV_TYPE_CSR_OFFSET 1 +#define CSR1212_KV_TYPE_LEAF 2 +#define CSR1212_KV_TYPE_DIRECTORY 3 + + +/* CSR 1212 key ids */ +#define CSR1212_KV_ID_DESCRIPTOR 0x01 +#define CSR1212_KV_ID_BUS_DEPENDENT_INFO 0x02 +#define CSR1212_KV_ID_VENDOR 0x03 +#define CSR1212_KV_ID_HARDWARE_VERSION 0x04 +#define CSR1212_KV_ID_MODULE 0x07 +#define CSR1212_KV_ID_NODE_CAPABILITIES 0x0C +#define CSR1212_KV_ID_EUI_64 0x0D +#define CSR1212_KV_ID_UNIT 0x11 +#define CSR1212_KV_ID_SPECIFIER_ID 0x12 +#define CSR1212_KV_ID_VERSION 0x13 +#define CSR1212_KV_ID_DEPENDENT_INFO 0x14 +#define CSR1212_KV_ID_UNIT_LOCATION 0x15 +#define CSR1212_KV_ID_MODEL 0x17 +#define CSR1212_KV_ID_INSTANCE 0x18 +#define CSR1212_KV_ID_KEYWORD 0x19 +#define CSR1212_KV_ID_FEATURE 0x1A +#define CSR1212_KV_ID_EXTENDED_ROM 0x1B +#define CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID 0x1C +#define CSR1212_KV_ID_EXTENDED_KEY 0x1D +#define CSR1212_KV_ID_EXTENDED_DATA 0x1E +#define CSR1212_KV_ID_MODIFIABLE_DESCRIPTOR 0x1F +#define CSR1212_KV_ID_DIRECTORY_ID 0x20 +#define CSR1212_KV_ID_REVISION 0x21 + + +/* IEEE 1212 Address space map */ +#define CSR1212_ALL_SPACE_BASE (0x000000000000ULL) +#define CSR1212_ALL_SPACE_SIZE (1ULL << 48) +#define CSR1212_ALL_SPACE_END (CSR1212_ALL_SPACE_BASE + CSR1212_ALL_SPACE_SIZE) + +#define CSR1212_MEMORY_SPACE_BASE (0x000000000000ULL) +#define CSR1212_MEMORY_SPACE_SIZE ((256ULL * (1ULL << 40)) - (512ULL * (1ULL << 20))) +#define CSR1212_MEMORY_SPACE_END (CSR1212_MEMORY_SPACE_BASE + CSR1212_MEMORY_SPACE_SIZE) + +#define CSR1212_PRIVATE_SPACE_BASE (0xffffe0000000ULL) +#define CSR1212_PRIVATE_SPACE_SIZE (256ULL * (1ULL << 20)) +#define CSR1212_PRIVATE_SPACE_END (CSR1212_PRIVATE_SPACE_BASE + CSR1212_PRIVATE_SPACE_SIZE) + +#define CSR1212_REGISTER_SPACE_BASE (0xfffff0000000ULL) +#define CSR1212_REGISTER_SPACE_SIZE (256ULL * (1ULL << 20)) +#define CSR1212_REGISTER_SPACE_END (CSR1212_REGISTER_SPACE_BASE + CSR1212_REGISTER_SPACE_SIZE) + +#define CSR1212_CSR_ARCH_REG_SPACE_BASE (0xfffff0000000ULL) +#define CSR1212_CSR_ARCH_REG_SPACE_SIZE (512) +#define CSR1212_CSR_ARCH_REG_SPACE_END (CSR1212_CSR_ARCH_REG_SPACE_BASE + CSR1212_CSR_ARCH_REG_SPACE_SIZE) +#define CSR1212_CSR_ARCH_REG_SPACE_OFFSET (CSR1212_CSR_ARCH_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) + +#define CSR1212_CSR_BUS_DEP_REG_SPACE_BASE (0xfffff0000200ULL) +#define CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE (512) +#define CSR1212_CSR_BUS_DEP_REG_SPACE_END (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE + CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE) +#define CSR1212_CSR_BUS_DEP_REG_SPACE_OFFSET (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) + +#define CSR1212_CONFIG_ROM_SPACE_BASE (0xfffff0000400ULL) +#define CSR1212_CONFIG_ROM_SPACE_SIZE (1024) +#define CSR1212_CONFIG_ROM_SPACE_END (CSR1212_CONFIG_ROM_SPACE_BASE + CSR1212_CONFIG_ROM_SPACE_SIZE) +#define CSR1212_CONFIG_ROM_SPACE_OFFSET (CSR1212_CONFIG_ROM_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) + +#define CSR1212_UNITS_SPACE_BASE (0xfffff0000800ULL) +#define CSR1212_UNITS_SPACE_SIZE ((256ULL * (1ULL << 20)) - 2048) +#define CSR1212_UNITS_SPACE_END (CSR1212_UNITS_SPACE_BASE + CSR1212_UNITS_SPACE_SIZE) +#define CSR1212_UNITS_SPACE_OFFSET (CSR1212_UNITS_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) + +#define CSR1212_EXTENDED_ROM_SIZE (0x10000 * sizeof(u_int32_t)) + + +/* Config ROM image structures */ +struct csr1212_bus_info_block_img { + u_int8_t length; + u_int8_t crc_length; + u_int16_t crc; + + /* Must be last */ + u_int32_t data[0]; /* older gcc can't handle [] which is standard */ +}; + +#define CSR1212_KV_KEY(quad) (CSR1212_BE32_TO_CPU(quad) >> CSR1212_KV_KEY_SHIFT) +#define CSR1212_KV_KEY_TYPE(quad) (CSR1212_KV_KEY(quad) >> CSR1212_KV_KEY_TYPE_SHIFT) +#define CSR1212_KV_KEY_ID(quad) (CSR1212_KV_KEY(quad) & CSR1212_KV_KEY_ID_MASK) +#define CSR1212_KV_VAL(quad) (CSR1212_BE32_TO_CPU(quad) & CSR1212_KV_VAL_MASK) + +#define CSR1212_SET_KV_KEY(quad, key) ((quad) = \ + CSR1212_CPU_TO_BE32(CSR1212_KV_VAL(quad) | ((key) << CSR1212_KV_KEY_SHIFT))) +#define CSR1212_SET_KV_VAL(quad, val) ((quad) = \ + CSR1212_CPU_TO_BE32((CSR1212_KV_KEY(quad) << CSR1212_KV_KEY_SHIFT) | (val))) +#define CSR1212_SET_KV_TYPEID(quad, type, id) ((quad) = \ + CSR1212_CPU_TO_BE32(CSR1212_KV_VAL(quad) | \ + (((((type) & CSR1212_KV_KEY_TYPE_MASK) << CSR1212_KV_KEY_TYPE_SHIFT) | \ + ((id) & CSR1212_KV_KEY_ID_MASK)) << CSR1212_KV_KEY_SHIFT))) + +typedef u_int32_t csr1212_quad_t; + + +struct csr1212_keyval_img { + u_int16_t length; + u_int16_t crc; + + /* Must be last */ + csr1212_quad_t data[0]; /* older gcc can't handle [] which is standard */ +}; + +struct csr1212_leaf { + int len; + u_int32_t *data; +}; + +struct csr1212_dentry { + struct csr1212_dentry *next, *prev; + struct csr1212_keyval *kv; +}; + +struct csr1212_directory { + int len; + struct csr1212_dentry *dentries_head, *dentries_tail; +}; + +struct csr1212_keyval { + struct { + u_int8_t type; + u_int8_t id; + } key; + union { + u_int32_t immediate; + u_int32_t csr_offset; + struct csr1212_leaf leaf; + struct csr1212_directory directory; + } value; + struct csr1212_keyval *associate; + int refcnt; + + /* used in generating and/or parsing CSR image */ + struct csr1212_keyval *next, *prev; /* flat list of CSR elements */ + u_int32_t offset; /* position in CSR from 0xffff f000 0000 */ + u_int8_t valid; /* flag indicating keyval has valid data*/ +}; + + +struct csr1212_cache_region { + struct csr1212_cache_region *next, *prev; + u_int32_t offset_start; /* inclusive */ + u_int32_t offset_end; /* exclusive */ +}; + +struct csr1212_csr_rom_cache { + struct csr1212_csr_rom_cache *next, *prev; + struct csr1212_cache_region *filled_head, *filled_tail; + struct csr1212_keyval *layout_head, *layout_tail; + size_t size; + u_int32_t offset; + struct csr1212_keyval *ext_rom; + size_t len; + + /* Must be last */ + u_int32_t data[0]; /* older gcc can't handle [] which is standard */ +}; + +struct csr1212_csr { + size_t bus_info_len; /* bus info block length in bytes */ + size_t crc_len; /* crc length in bytes */ + u_int32_t *bus_info_data; /* bus info data incl bus name and EUI */ + + void *private_data; /* private_data, bus specific data */ + struct csr1212_bus_ops *ops; + + struct csr1212_keyval *root_kv; + + int max_rom; /* max bytes readable in Config ROM region */ + + /* Items below used for image parsing and generation */ + struct csr1212_csr_rom_cache *cache_head, *cache_tail; +}; + +struct csr1212_bus_ops { + /* This function is used by csr1212 to read additional information + * from remote nodes when parsing a Config ROM (i.e., read Config ROM + * entries located in the Units Space. Must return 0 on success + * anything else indicates an error. */ + int (*bus_read) (struct csr1212_csr *csr, u_int64_t addr, + u_int16_t length, void *buffer, void *private_data); + + /* This function is used by csr1212 to allocate a region in units space + * in the event that Config ROM entries don't all fit in the predefined + * 1K region. The void *private_data parameter is private_data member of struct + * csr1212_csr. */ + u_int64_t (*allocate_addr_range) (u_int64_t size, u_int32_t alignment, + void *private_data); + + + /* This function is used by csr1212 to release a region in units space + * that is no longer needed. */ + void (*release_addr) (u_int64_t addr, void *private_data); + + /* This function is used by csr1212 to determine the max read request + * supported by a remote node when reading the ConfigROM space. Must + * return 0, 1, or 2 per IEEE 1212. */ + int (*get_max_rom) (u_int32_t *bus_info, void *private_data); +}; + + + + +/* Descriptor Leaf manipulation macros */ +#define CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT 24 +#define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK 0xffffff +#define CSR1212_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u_int32_t)) + +#define CSR1212_DESCRIPTOR_LEAF_TYPE(kv) \ + (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[0]) >> CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) +#define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) \ + (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[0]) & \ + CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK) +#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \ + (&((kv)->value.leaf.data[1])) + +#define CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, type) \ + ((kv)->value.leaf.data[0] = \ + CSR1212_CPU_TO_BE32(CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) | \ + ((type) << CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT))) +#define CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, spec_id) \ + ((kv)->value.leaf.data[0] = \ + CSR1212_CPU_TO_BE32((CSR1212_DESCRIPTOR_LEAF_TYPE(kv) << \ + CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) | \ + ((spec_id) & CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK))) + +/* Text Descriptor Leaf manipulation macros */ +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT 28 +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK 0xf /* after shift */ +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT 16 +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK 0xfff /* after shift */ +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u_int32_t)) + +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) \ + (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) >> \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT) +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) \ + ((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) >> \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT) & \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) \ + (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) & \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK) +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv) \ + (&((kv)->value.leaf.data[2])) + +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, width) \ + ((kv)->value.leaf.data[1] = \ + ((kv)->value.leaf.data[1] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK << \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT))) | \ + CSR1212_CPU_TO_BE32(((width) & \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK) << \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT)) +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, char_set) \ + ((kv)->value.leaf.data[1] = \ + ((kv)->value.leaf.data[1] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK << \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT))) | \ + CSR1212_CPU_TO_BE32(((char_set) & \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) << \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT)) +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language) \ + ((kv)->value.leaf.data[1] = \ + ((kv)->value.leaf.data[1] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK))) | \ + CSR1212_CPU_TO_BE32(((language) & \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK))) + + +/* Icon Descriptor Leaf manipulation macros */ +#define CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK 0xffffff +#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT 30 +#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK 0x3 /* after shift */ +#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT 16 +#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK 0xf /* after shift */ +#define CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff +#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT 16 +#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK 0xffff /* after shift */ +#define CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK 0xffff +#define CSR1212_ICON_DESCRIPTOR_LEAF_OVERHEAD (3 * sizeof(u_int32_t)) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_VERSION(kv) \ + (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[2]) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH(kv) \ + (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) >> \ + CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE(kv) \ + ((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) >> \ + CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE(kv) \ + (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN(kv) \ + ((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[4]) >> \ + CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_HSCAN_SHIFT) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_HSCAN_MASK) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN(kv) \ + (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[4]) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE(kv) \ + (&((kv)->value.leaf.data[5])) + +static inline u_int32_t *CSR1212_ICON_DESCRIPTOR_LEAF_PIXELS(struct csr1212_keyval *kv) +{ + static const int pd[4] = { 0, 4, 16, 256 }; + static const int cs[16] = { 4, 2 }; + int ps = pd[CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH(kv)]; + + return &kv->value.leaf.data[5 + + (ps * cs[CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE(kv)]) / + sizeof(u_int32_t)]; +} + +#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_VERSION(kv, version) \ + ((kv)->value.leaf.data[2] = \ + ((kv)->value.leaf.data[2] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK))) | \ + CSR1212_CPU_TO_BE32(((version) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK))) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_PALETTE_DEPTH(kv, palette_depth) \ + ((kv)->value.leaf.data[3] = \ + ((kv)->value.leaf.data[3] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK << \ + CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT))) | \ + CSR1212_CPU_TO_BE32(((palette_depth) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK) << \ + CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT)) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_COLOR_SPACE(kv, color_space) \ + ((kv)->value.leaf.data[3] = \ + ((kv)->value.leaf.data[3] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK << \ + CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT))) | \ + CSR1212_CPU_TO_BE32(((color_space) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK) << \ + CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT)) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language) \ + ((kv)->value.leaf.data[3] = \ + ((kv)->value.leaf.data[3] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK))) | \ + CSR1212_CPU_TO_BE32(((language) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK))) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_HSCAN(kv, hscan) \ + ((kv)->value.leaf.data[4] = \ + ((kv)->value.leaf.data[4] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK << \ + CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT))) | \ + CSR1212_CPU_TO_BE32(((hscan) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK) << \ + CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT)) + +#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_VSCAN(kv, vscan) \ + ((kv)->value.leaf.data[4] = \ + (((kv)->value.leaf.data[4] & \ + CSR1212_CPU_TO_BE32(~CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK))) | \ + CSR1212_CPU_TO_BE32(((vscan) & \ + CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK))) + + +/* Modifiable Descriptor Leaf manipulation macros */ +#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT 16 +#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK 0xffff +#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_SHIFT 32 +#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK 0xffff +#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_LO_MASK 0xffffffffULL + +#define CSR1212_MODIFIABLE_DESCRIPTOR_MAX_SIZE(kv) \ + CSR1212_BE16_TO_CPU((kv)->value.leaf.data[0] >> CSR1212_MODIFIABLE_DESCRIPTOR_MAX_SIZE_SHIFT) + +#define CSR1212_MODIFIABLE_DESCRIPTOR_ADDRESS(kv) \ + (CSR1212_BE16_TO_CPU(((u_int64_t)((kv)->value.leaf.data[0])) << \ + CSR1212_MODIFIABLE_DESCRIPTOR_ADDR_HI_SHIFT) | \ + CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1])) + +#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_MAX_SIZE(kv, size) \ + ((kv)->value.leaf.data[0] = \ + ((kv)->value.leaf.data[0] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK << \ + CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT))) | \ + CSR1212_CPU_TO_BE32(((size) & \ + CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK) << \ + CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT)) + +#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_HI(kv, addr) \ + ((kv)->value.leaf.data[0] = \ + ((kv)->value.leaf.data[0] & \ + CSR1212_CPU_TO_BE32(~(CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK))) | \ + CSR1212_CPU_TO_BE32(((addr) & \ + CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK))) + +#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_LO(kv, addr) \ + ((kv)->value.leaf.data[1] = \ + CSR1212_CPU_TO_BE32(addr & CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_LO_MASK)) + + + +/* The following 2 function are for creating new Configuration ROM trees. The + * first function is used for both creating local trees and parsing remote + * trees. The second function adds pertinent information to local Configuration + * ROM trees - namely data for the bus information block. */ +extern struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops, + size_t bus_info_size, + void *private_data); +extern void csr1212_init_local_csr(struct csr1212_csr *csr, + const u_int32_t *bus_info_data, int max_rom); + + +/* The following function destroys a Configuration ROM tree and release all + * memory taken by the tree. */ +extern void csr1212_destroy_csr(struct csr1212_csr *csr); + + +/* The following set of functions are fore creating new keyvals for placement in + * a Configuration ROM tree. Code that creates new keyvals with these functions + * must release those keyvals with csr1212_release_keyval() when they are no + * longer needed. */ +extern struct csr1212_keyval *csr1212_new_immediate(u_int8_t key, u_int32_t value); +extern struct csr1212_keyval *csr1212_new_leaf(u_int8_t key, const void *data, + size_t data_len); +extern struct csr1212_keyval *csr1212_new_csr_offset(u_int8_t key, + u_int32_t csr_offset); +extern struct csr1212_keyval *csr1212_new_directory(u_int8_t key); +extern struct csr1212_keyval *csr1212_new_extended_immediate(u_int32_t spec, + u_int32_t key, + u_int32_t value); +extern struct csr1212_keyval *csr1212_new_extended_leaf(u_int32_t spec, + u_int32_t key, + const void *data, + size_t data_len); +extern struct csr1212_keyval *csr1212_new_descriptor_leaf(u_int8_t dtype, + u_int32_t specifier_id, + const void *data, + size_t data_len); +extern struct csr1212_keyval *csr1212_new_textual_descriptor_leaf(u_int8_t cwidth, + u_int16_t cset, + u_int16_t language, + const void *data, + size_t data_len); +extern struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s); +extern struct csr1212_keyval *csr1212_new_icon_descriptor_leaf(u_int32_t version, + u_int8_t palette_depth, + u_int8_t color_space, + u_int16_t language, + u_int16_t hscan, + u_int16_t vscan, + u_int32_t *palette, + u_int32_t *pixels); +extern struct csr1212_keyval *csr1212_new_modifiable_descriptor_leaf(u_int16_t max_size, + u_int64_t address); +extern struct csr1212_keyval *csr1212_new_keyword_leaf(int strc, + const char *strv[]); + + +/* The following functions manage association between keyvals. Typically, + * Descriptor Leaves and Directories will be associated with another keyval and + * it is desirable for the Descriptor keyval to be place immediately after the + * keyval that it is associated with.*/ +extern int csr1212_associate_keyval(struct csr1212_keyval *kv, + struct csr1212_keyval *associate); +extern void csr1212_disassociate_keyval(struct csr1212_keyval *kv); + + +/* The following functions manage the association of a keyval and directories. + * A keyval may be attached to more than one directory. */ +extern int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, + struct csr1212_keyval *kv); +extern void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir, + struct csr1212_keyval *kv); + + +/* The following functions create a Configuration ROM image from the tree of + * keyvals provided. csr1212_generate_csr_image() creates a complete image in + * the list of caches available via csr->cache_head. The other functions are + * provided should there be a need to create a flat image without restrictions + * placed by IEEE 1212. */ +extern struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, + struct csr1212_keyval *start_kv, + int start_pos); +extern size_t csr1212_generate_layout_order(struct csr1212_keyval *kv); +extern void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache); +extern int csr1212_generate_csr_image(struct csr1212_csr *csr); + + +/* This is a convience function for reading a block of data out of one of the + * caches in the csr->cache_head list. */ +extern int csr1212_read(struct csr1212_csr *csr, u_int32_t offset, void *buffer, + u_int32_t len); + + +/* The following functions are in place for parsing Configuration ROM images. + * csr1212_parse_keyval() is used should there be a need to directly parse a + * Configuration ROM directly. */ +extern int csr1212_parse_keyval(struct csr1212_keyval *kv, + struct csr1212_csr_rom_cache *cache); +extern int csr1212_parse_csr(struct csr1212_csr *csr); + +/* These are internal functions referenced by inline functions below. */ +extern int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv); +extern void _csr1212_destroy_keyval(struct csr1212_keyval *kv); + + +/* This function allocates a new cache which may be used for either parsing or + * generating sub-sets of Configuration ROM images. */ +static inline struct csr1212_csr_rom_cache *csr1212_rom_cache_malloc(u_int32_t offset, + size_t size) +{ + struct csr1212_csr_rom_cache *cache; + + cache = (struct csr1212_csr_rom_cache*)CSR1212_MALLOC(sizeof(struct csr1212_csr_rom_cache) + size); + if (!cache) + return NULL; + + cache->next = NULL; + cache->prev = NULL; + cache->filled_head = NULL; + cache->filled_tail = NULL; + cache->layout_head = NULL; + cache->layout_tail = NULL; + cache->offset = offset; + cache->size = size; + cache->ext_rom = NULL; + + return cache; +} + + +/* This function ensures that a keyval contains data when referencing a keyval + * created by parsing a Configuration ROM. */ +static inline struct csr1212_keyval *csr1212_get_keyval(struct csr1212_csr *csr, + struct csr1212_keyval *kv) +{ + if (!kv) + return NULL; + if (!kv->valid) + if (_csr1212_read_keyval(csr, kv) != CSR1212_SUCCESS) + return NULL; + return kv; +} + + +/* This function increments the reference count for a keyval should there be a + * need for code to retain a keyval that has been parsed. */ +static inline void csr1212_keep_keyval(struct csr1212_keyval *kv) +{ + kv->refcnt++; +} + + +/* This function decrements a keyval's reference count and will destroy the + * keyval when there are no more users of the keyval. This should be called by + * any code that calls csr1212_keep_keyval() or any of the keyval creation + * routines csr1212_new_*(). */ +static inline void csr1212_release_keyval(struct csr1212_keyval *kv) +{ + if (kv->refcnt > 1) + kv->refcnt--; + else + _csr1212_destroy_keyval(kv); +} + + +/* + * This macro allows for looping over the keyval entries in a directory and it + * ensures that keyvals from remote ConfigROMs are parsed properly. + * + * _csr is a struct csr1212_csr * that points to CSR associated with dir. + * _kv is a struct csr1212_keyval * that'll point to the current keyval (loop index). + * _dir is a struct csr1212_keyval * that points to the directory to be looped. + * _pos is a struct csr1212_dentry * that is used internally for indexing. + * + * kv will be NULL upon exit of the loop. + */ +#define csr1212_for_each_dir_entry(_csr, _kv, _dir, _pos) \ + for (csr1212_get_keyval((_csr), (_dir)), \ + _pos = (_dir)->value.directory.dentries_head, \ + _kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : NULL; \ + (_kv) && (_pos); \ + (_kv->associate == NULL) ? \ + ((_pos = _pos->next), \ + (_kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : \ + NULL)) : \ + (_kv = csr1212_get_keyval((_csr), _kv->associate))) + + + +#ifdef __cplusplus + } +#endif + +#endif /* __CSR1212_H__ */ Index: /branches/libffado-2.0/src/libieee1394/cycletimer.h =================================================================== --- /branches/libffado-2.0/src/libieee1394/cycletimer.h (revision 1090) +++ /branches/libffado-2.0/src/libieee1394/cycletimer.h (revision 1090) @@ -0,0 +1,540 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +/* Definitions and utility macro's to handle the ISO cycle timer */ + +#ifndef __CYCLETIMER_H__ +#define __CYCLETIMER_H__ + +#include "debugmodule/debugmodule.h" + +#include + +#define CSR_CYCLE_TIME 0x200 +#define CSR_REGISTER_BASE 0xfffff0000000ULL + +#define CYCLES_PER_SECOND 8000U +#define TICKS_PER_CYCLE 3072U +#define TICKS_PER_HALFCYCLE (3072U/2U) +#define TICKS_PER_SECOND 24576000UL +#define TICKS_PER_USEC (24.576000) + +#define USECS_PER_TICK (1.0/TICKS_PER_USEC) +#define USECS_PER_CYCLE (125U) + +#define CYCLE_TIMER_GET_SECS(x) ((((x) & 0xFE000000UL) >> 25)) +#define CYCLE_TIMER_GET_CYCLES(x) ((((x) & 0x01FFF000UL) >> 12)) +#define CYCLE_TIMER_GET_OFFSET(x) ((((x) & 0x00000FFFUL))) +#define CYCLE_TIMER_TO_TICKS(x) ((CYCLE_TIMER_GET_SECS(x) * TICKS_PER_SECOND) +\ + (CYCLE_TIMER_GET_CYCLES(x) * TICKS_PER_CYCLE ) +\ + (CYCLE_TIMER_GET_OFFSET(x) )) + +// non-efficient versions, to be avoided in critical code +#define TICKS_TO_SECS(x) ((x)/TICKS_PER_SECOND) +#define TICKS_TO_CYCLES(x) (((x)/TICKS_PER_CYCLE) % CYCLES_PER_SECOND) +#define TICKS_TO_OFFSET(x) (((x)%TICKS_PER_CYCLE)) + +#define TICKS_TO_CYCLE_TIMER(x) ( ((TICKS_TO_SECS(x) & 0x7F) << 25) \ + | ((TICKS_TO_CYCLES(x) & 0x1FFF) << 12) \ + | ((TICKS_TO_OFFSET(x) & 0xFFF))) + +#define TICKS_TO_SYT(x) (((TICKS_TO_CYCLES(x) & 0xF) << 12) \ + | ((TICKS_TO_OFFSET(x) & 0xFFF))) + +#define CYCLE_TIMER_UNWRAP_TICKS(x) (((uint64_t)(x)) \ + + (127ULL * TICKS_PER_SECOND) \ + + (CYCLES_PER_SECOND * TICKS_PER_CYCLE) \ + + (TICKS_PER_CYCLE) \ + ) +#define CYCLE_TIMER_WRAP_TICKS(x) ((x % TICKS_PER_SECOND)) + +#define INVALID_TIMESTAMP_TICKS 0xFFFFFFFFFFFFFFFFULL + +DECLARE_GLOBAL_DEBUG_MODULE; + +/** + * @brief Wraps x to the maximum number of ticks + * + * The input value is wrapped to the maximum value of the cycle + * timer, in ticks (128sec * 24576000 ticks/sec). + * + * @param x time to wrap + * @return wrapped time + */ +static inline uint64_t wrapAtMaxTicks(uint64_t x) { + if (x >= TICKS_PER_SECOND * 128L) { + x -= TICKS_PER_SECOND * 128L; + } + +#ifdef DEBUG + if (x >= TICKS_PER_SECOND * 128L) { + debugWarning("insufficient wrapping: %llu\n",x); + } +#endif + + return x; +} + +/** + * @brief Wraps x to the minimum number of ticks + * + * The input value is wrapped to the minimum value of the cycle + * timer, in ticks (= 0). + * + * @param x time to wrap + * @return wrapped time + */ +static inline int64_t wrapAtMinTicks(int64_t x) { + if (x < 0) { + x += TICKS_PER_SECOND * 128L; + } + +#ifdef DEBUG + if (x < 0) { + debugWarning("insufficient wrapping: %lld\n",x); + + debugWarning("correcting...\n"); + while (x < 0) { + x += TICKS_PER_SECOND * 128L; + + if (x < 0) { + debugWarning(" insufficient wrapping: %lld\n",x); + } + } + } + +#endif + + return (int64_t)x; +} + +/** + * @brief Wraps both at minimum and maximum value for ticks + * + * The input value is wrapped to the maximum value of the cycle + * timer, in ticks (128sec * 24576000 ticks/sec), and + * to the minimum value of the cycle timer, in ticks (= 0). + * + * @param x value to wrap + * @return wrapped value + */ +static inline int64_t wrapAtMinMaxTicks(int64_t x) { + + if (x < 0) { + x += TICKS_PER_SECOND * 128L; + } else if (x >= (int64_t)(TICKS_PER_SECOND * 128L)) { + x -= TICKS_PER_SECOND * 128L; + } + +#ifdef DEBUG + if (x >= (int64_t)(TICKS_PER_SECOND * 128L)) { + debugWarning("insufficient wrapping (max): %llu\n",x); + } + if (x < 0) { + debugWarning("insufficient wrapping (min): %lld\n",x); + } +#endif + return x; + +} + +/** + * @brief Computes the sum of two cycle values + * + * This function computes a sum between cycles + * such that it respects wrapping (at 8000 cycles). + * + * The passed arguments are assumed to be valid cycle numbers, + * i.e. they should be wrapped at 8000 cycles + * + * See addTicks + * + * @param x First cycle value + * @param y Second cycle value + * @return the sum x+y, wrapped + */ +static inline unsigned int addCycles(unsigned int x, unsigned int y) { + unsigned int sum = x + y; +#ifdef DEBUG + if (x >= CYCLES_PER_SECOND || y >= CYCLES_PER_SECOND ) { + debugWarning("At least one argument not wrapped correctly: x=%u, y=%u\n",x,y); + } +#endif + + // since both x and y are < CYCLES_PER_SECOND this should be enough to unwrap + if (sum > CYCLES_PER_SECOND) sum -= CYCLES_PER_SECOND; + return sum; +} + +/** + * @brief Computes a difference between cycles + * + * This function computes a difference between cycles + * such that it respects wrapping (at 8000 cycles). + * + * See diffTicks + * + * @param x First cycle value + * @param y Second cycle value + * @return the difference x-y, unwrapped + */ +static inline int diffCycles(unsigned int x, unsigned int y) { + int diff = (int)x - (int)y; + + // the maximal difference we allow (4000 cycles) + const int max=CYCLES_PER_SECOND/2; + + if(diff > max) { + diff -= CYCLES_PER_SECOND; + } else if (diff < -max) { + diff += CYCLES_PER_SECOND; + } + + return diff; +} + +/** + * @brief Computes a difference between timestamps + * + * This function computes a difference between timestamps + * such that it respects wrapping. + * + * If x wraps around, but y doesn't, the result of x-y is + * negative and very large. However the real difference is + * not large. It can be calculated by unwrapping x and then + * calculating x-y. + * + * @param x First timestamp + * @param y Second timestamp + * @return the difference x-y, unwrapped + */ +static inline int64_t diffTicks(int64_t x, int64_t y) { + int64_t diff=(int64_t)x - (int64_t)y; + + // the maximal difference we allow (64secs) + const int64_t wrapvalue=((int64_t)TICKS_PER_SECOND)*128LL; + const int64_t max=wrapvalue/2LL; + + if(diff > max) { + // this means that y has wrapped, but + // x has not. we should unwrap y + // by adding TICKS_PER_SECOND*128L, meaning that we should substract + // this value from diff + diff -= wrapvalue; + } else if (diff < -max) { + // this means that x has wrapped, but + // y has not. we should unwrap x + // by adding TICKS_PER_SECOND*128L, meaning that we should add + // this value to diff + diff += wrapvalue; + } + +#ifdef DEBUG + if(diff > max || diff < -max) { + debugWarning("difference does not make any sense\n"); + debugWarning("diff=%lld max=%lld\n", diff, max); + + } +#endif + + return (int64_t)diff; + +} + +/** + * @brief Computes a sum of timestamps + * + * This function computes a sum of timestamps in ticks, + * wrapping the result if necessary. + * + * @param x First timestamp + * @param y Second timestamp + * @return the sum x+y, wrapped + */ +static inline uint64_t addTicks(uint64_t x, uint64_t y) { + uint64_t sum=x+y; + + return wrapAtMaxTicks(sum); +} + +/** + * @brief Computes a substraction of timestamps + * + * This function computes a substraction of timestamps in ticks, + * wrapping the result if necessary. + * + * @param x First timestamp + * @param y Second timestamp + * @return the difference x-y, wrapped + */ +static inline uint64_t substractTicks(uint64_t x, uint64_t y) { + int64_t subs=x-y; + + return wrapAtMinTicks(subs); +} + +/** + * @brief Converts a received SYT timestamp to a full timestamp in ticks. + * + * + * @param syt_timestamp The SYT timestamp as present in the packet + * @param rcv_cycle The cycle this timestamp was received on + * @param ctr_now The current value of the cycle timer ('now') + * @return + */ +static inline uint64_t sytRecvToFullTicks(uint64_t syt_timestamp, unsigned int rcv_cycle, uint64_t ctr_now) { + uint64_t timestamp; + + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "SYT=%04llX CY=%u CTR=%08llX\n", + syt_timestamp, rcv_cycle, ctr_now); + + // reconstruct the full cycle + uint64_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now); + uint64_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now); + + // check for bogus ctr + // the cycle timer should be ahead of the receive timer + int diff_cycles = diffCycles(cc_cycles, rcv_cycle); + if (diff_cycles<0) { + debugWarning("current cycle timer not ahead of receive cycle: rcv: %u / cc: %llu (%d)\n", + rcv_cycle, cc_cycles, diff_cycles); + } + + // the cycletimer has wrapped since this packet was received + // we want cc_seconds to reflect the 'seconds' at the point this + // was received + if (rcv_cycle>cc_cycles && (diff_cycles>=0)) { + if (cc_seconds) { + cc_seconds--; + } else { + // seconds has wrapped around, so we'd better not substract 1 + // the good value is 127 + cc_seconds=127; + } + } + + // reconstruct the top part of the timestamp using the current cycle number + uint64_t rcv_cycle_masked=rcv_cycle & 0xF; + uint64_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp); + + // if this is true, wraparound has occurred, undo this wraparound + if(syt_cycle= 8000) { + debugWarning("insufficient unwrapping\n"); + } +#endif + timestamp = new_cycles * TICKS_PER_CYCLE; + // add one second due to wraparound + timestamp += TICKS_PER_SECOND; + } + + timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp); + + timestamp = addTicks(timestamp, cc_seconds * TICKS_PER_SECOND); + + #ifdef DEBUG + if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) { + debugWarning("back-converted timestamp not equal to SYT\n"); + debugWarning("TS=%011llu TSC=%08lX SYT=%04X\n", + timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp); + } + #endif + + return timestamp; +} + +/** + * @brief Converts a received SYT timestamp to a full timestamp in ticks. + * + * + * @param syt_timestamp The SYT timestamp as present in the packet + * @param rcv_ctr The CTR value this timestamp was received on (offset can be 0) + * @return + */ +static inline uint64_t sytRecvToFullTicks2(uint64_t syt_timestamp, uint32_t rcv_ctr) { + uint64_t timestamp; + + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "SYT=%04llX RCV_CTR=%08X\n", + syt_timestamp, rcv_ctr); + + // reconstruct the top part of the timestamp using the current cycle number + unsigned int rcv_cycle = CYCLE_TIMER_GET_CYCLES(rcv_ctr); + unsigned int rcv_cycle_masked = rcv_cycle & 0xF; + unsigned int syt_cycle = CYCLE_TIMER_GET_CYCLES(syt_timestamp); + + // if this is true, wraparound has occurred, undo this wraparound + if(syt_cycle= 8000) { + debugWarning("insufficient unwrapping\n"); + } +#endif + timestamp = rcv_cycle * TICKS_PER_CYCLE; + // add one second due to wraparound + timestamp += TICKS_PER_SECOND; + } + + timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp); + + timestamp = addTicks(timestamp, CYCLE_TIMER_GET_SECS(rcv_ctr) * TICKS_PER_SECOND); + + #ifdef DEBUG + if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) { + debugWarning("back-converted timestamp not equal to SYT\n"); + debugWarning("TS=%011llu TSC=%08lX SYT=%04X\n", + timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp); + } + #endif + + return timestamp; +} + +/** + * @brief Converts a transmit SYT timestamp to a full timestamp in ticks. + * + * The difference between sytRecvToFullTicks and sytXmitToFullTicks is + * the way SYT cycle wraparound is detected: in the receive version, + * wraparound is present if rcv_cycle > current_cycle. In the xmit + * version this is when current_cycle > xmt_cycle. + * + * @param syt_timestamp The SYT timestamp as present in the packet + * @param xmt_cycle The cycle this timestamp was received on + * @param ctr_now The current value of the cycle timer ('now') + * @return + */ +static inline uint64_t sytXmitToFullTicks(uint64_t syt_timestamp, unsigned int xmt_cycle, uint64_t ctr_now) { + uint64_t timestamp; + + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "SYT=%08llX CY=%04X CTR=%08llX\n", + syt_timestamp, xmt_cycle, ctr_now); + + // reconstruct the full cycle + uint64_t cc_cycles=CYCLE_TIMER_GET_CYCLES(ctr_now); + uint64_t cc_seconds=CYCLE_TIMER_GET_SECS(ctr_now); + + // check for bogus CTR + int diff_cycles = diffCycles(xmt_cycle, cc_cycles); + if (diff_cycles<0) { + debugWarning("xmit cycle not ahead of current cycle: xmt: %u / cc: %llu (%d)\n", + xmt_cycle, cc_cycles, diff_cycles); + } + + // the cycletimer has wrapped since this packet was received + // we want cc_seconds to reflect the 'seconds' at the point this + // is to be transmitted + if (cc_cycles>xmt_cycle && (diff_cycles>=0)) { + if (cc_seconds) { + cc_seconds--; + } else { + // seconds has wrapped around, so we'd better not substract 1 + // the good value is 127 + cc_seconds=127; + } + } + + // reconstruct the top part of the timestamp using the current cycle number + uint64_t xmt_cycle_masked=xmt_cycle & 0xF; + uint64_t syt_cycle=CYCLE_TIMER_GET_CYCLES(syt_timestamp); + + // if this is true, wraparound has occurred, undo this wraparound + if(syt_cycle= 8000) { + debugWarning("insufficient unwrapping\n"); + } +#endif + timestamp = new_cycles * TICKS_PER_CYCLE; + // add one second due to wraparound + timestamp += TICKS_PER_SECOND; + } + + timestamp += CYCLE_TIMER_GET_OFFSET(syt_timestamp); + + timestamp = addTicks(timestamp, cc_seconds * TICKS_PER_SECOND); + + #ifdef DEBUG + if(( TICKS_TO_CYCLE_TIMER(timestamp) & 0xFFFF) != syt_timestamp) { + debugWarning("back-converted timestamp not equal to SYT\n"); + debugWarning("TS=%011llu TSC=%08lX SYT=%04X\n", + timestamp, TICKS_TO_CYCLE_TIMER(timestamp), syt_timestamp); + } + #endif + + return timestamp; +} + +#endif // __CYCLETIMER_H__ Index: /branches/libffado-2.0/src/libieee1394/CycleTimerHelper.h =================================================================== --- /branches/libffado-2.0/src/libieee1394/CycleTimerHelper.h (revision 1080) +++ /branches/libffado-2.0/src/libieee1394/CycleTimerHelper.h (revision 1080) @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ +#ifndef __CYCLETIMERHELPER_H__ +#define __CYCLETIMERHELPER_H__ + +/** + * Implements a DLL based mechanism to track the cycle timer + * register of the Ieee1394Service pointed to by the 'parent'. + * + * A DLL mechanism is performance-wise better suited, since it + * does not require an OS call. Hence we run a thread to update + * the DLL at regular time intervals, and then use the DLL to + * generate a cycle timer estimate for the parent to pass on + * to it's clients. + * + * The idea is to make reading the cycle timer real-time safe, + * which isn't (necessarily)the case for the direct raw1394 call, + * since it's a kernel call that could block (although the current + * implementation is RT safe). + * + * This also allows us to run on systems not having the + * raw1394_read_cycle_timer kernel call. We can always do a normal + * read of our own cycle timer register, but that's not very accurate. + * The accuracy is improved by this DLL mechanism. Still not as good + * as when using the raw1394_read_cycle_timer call, but anyway. + * + * On the long run this code will also allow us to map system time + * on to 1394 time for the current host controller, hence enabling + * different clock domains to operate together. + */ + +#include "libutil/Thread.h" +#include "libutil/SystemTimeSource.h" +#include "cycletimer.h" + +#include "libutil/Functors.h" +#include "libutil/Mutex.h" + +#include "debugmodule/debugmodule.h" + +class Ieee1394Service; + +class CycleTimerHelper : public Util::RunnableInterface +{ +public: + CycleTimerHelper(Ieee1394Service &, unsigned int); + CycleTimerHelper(Ieee1394Service &, unsigned int, bool rt, int prio); + ~CycleTimerHelper(); + + virtual bool Init(); + virtual bool Execute(); + + bool setThreadParameters(bool rt, int priority); + bool Start(); + + /** + * @brief get the current cycle timer value (in ticks) + * @note thread safe + */ + uint32_t getCycleTimerTicks(); + + /** + * @brief get the cycle timer value for a specific time instant (in ticks) + * @note thread safe + */ + uint32_t getCycleTimerTicks(uint64_t now); + + /** + * @brief get the current cycle timer value (in CTR format) + * @note thread safe + */ + uint32_t getCycleTimer(); + + /** + * @brief get the cycle timer value for a specific time instant (in CTR format) + * @note thread safe + */ + uint32_t getCycleTimer(uint64_t now); + + float getRate(); + float getNominalRate(); + + /** + * @brief handle a bus reset + */ + void busresetHandler(); + + void setVerboseLevel(int l); + +private: + bool readCycleTimerWithRetry(uint32_t *cycle_timer, uint64_t *local_time, int ntries); + bool initValues(); + +#if IEEE1394SERVICE_USE_CYCLETIMER_DLL + bool initDLL(); +#endif + + Ieee1394Service &m_Parent; + // parameters + uint32_t m_ticks_per_update; + uint32_t m_usecs_per_update; + + float m_avg_wakeup_delay; + + // state variables + double m_dll_e2; + + double m_current_time_usecs; + double m_next_time_usecs; + double m_current_time_ticks; + double m_next_time_ticks; + bool m_first_run; + ffado_microsecs_t m_sleep_until; + + uint32_t m_cycle_timer_prev; + uint64_t m_cycle_timer_ticks_prev; + int m_high_bw_updates; + + // cached vars used for computation + struct compute_vars { + uint64_t usecs; + uint64_t ticks; + double rate; + }; + + #define CTRHELPER_NB_SHADOW_VARS 8 + struct compute_vars m_shadow_vars[CTRHELPER_NB_SHADOW_VARS]; + volatile unsigned int m_current_shadow_idx; + + // Threading + Util::Thread * m_Thread; + bool m_realtime; + unsigned int m_priority; + Util::Mutex* m_update_lock; + + // busreset handling + Util::Functor* m_busreset_functor; + bool m_unhandled_busreset; + +#ifdef DEBUG + uint64_t m_last_loop_entry; + int m_successive_short_loops; +#endif + + // debug stuff + DECLARE_DEBUG_MODULE; +}; +#endif Index: /branches/libffado-2.0/src/libieee1394/ieee1394service.cpp =================================================================== --- /branches/libffado-2.0/src/libieee1394/ieee1394service.cpp (revision 1163) +++ /branches/libffado-2.0/src/libieee1394/ieee1394service.cpp (revision 1163) @@ -0,0 +1,1141 @@ +/* + * Copyright (C) 2005-2008 by Daniel Wagner + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "config.h" +#include "ieee1394service.h" +#include "ARMHandler.h" +#include "cycletimer.h" +#include "IsoHandlerManager.h" +#include "CycleTimerHelper.h" + +#include +#include +#include + +#include "libutil/SystemTimeSource.h" +#include "libutil/Watchdog.h" +#include "libutil/PosixMutex.h" + +#include +#include "libutil/ByteSwap.h" + +#include + +#include +#include + +IMPL_DEBUG_MODULE( Ieee1394Service, Ieee1394Service, DEBUG_LEVEL_NORMAL ); + +Ieee1394Service::Ieee1394Service() + : m_handle( 0 ) + , m_handle_lock( new Util::PosixMutex() ) + , m_resetHandle( 0 ) + , m_util_handle( 0 ) + , m_port( -1 ) + , m_RHThread_lock( new Util::PosixMutex() ) + , m_threadRunning( false ) + , m_realtime ( false ) + , m_base_priority ( 0 ) + , m_pIsoManager( new IsoHandlerManager( *this ) ) + , m_pCTRHelper ( new CycleTimerHelper( *this, IEEE1394SERVICE_CYCLETIMER_DLL_UPDATE_INTERVAL_USEC ) ) + , m_have_new_ctr_read ( false ) + , m_pWatchdog ( new Util::Watchdog() ) +{ + for (unsigned int i=0; i<64; i++) { + m_channels[i].channel=-1; + m_channels[i].bandwidth=-1; + m_channels[i].alloctype=AllocFree; + m_channels[i].xmit_node=0xFFFF; + m_channels[i].xmit_plug=-1; + m_channels[i].recv_node=0xFFFF; + m_channels[i].recv_plug=-1; + } +} + +Ieee1394Service::Ieee1394Service(bool rt, int prio) + : m_handle( 0 ) + , m_handle_lock( new Util::PosixMutex() ) + , m_resetHandle( 0 ) + , m_util_handle( 0 ) + , m_port( -1 ) + , m_RHThread_lock( new Util::PosixMutex() ) + , m_threadRunning( false ) + , m_realtime ( rt ) + , m_base_priority ( prio ) + , m_pIsoManager( new IsoHandlerManager( *this, rt, prio ) ) + , m_pCTRHelper ( new CycleTimerHelper( *this, IEEE1394SERVICE_CYCLETIMER_DLL_UPDATE_INTERVAL_USEC, + rt && IEEE1394SERVICE_CYCLETIMER_HELPER_RUN_REALTIME, + prio + IEEE1394SERVICE_CYCLETIMER_HELPER_PRIO_INCREASE ) ) + , m_have_new_ctr_read ( false ) + , m_pWatchdog ( new Util::Watchdog() ) +{ + for (unsigned int i=0; i<64; i++) { + m_channels[i].channel=-1; + m_channels[i].bandwidth=-1; + m_channels[i].alloctype=AllocFree; + m_channels[i].xmit_node=0xFFFF; + m_channels[i].xmit_plug=-1; + m_channels[i].recv_node=0xFFFF; + m_channels[i].recv_plug=-1; + } +} + +Ieee1394Service::~Ieee1394Service() +{ + delete m_pIsoManager; + delete m_pCTRHelper; + stopRHThread(); + for ( arm_handler_vec_t::iterator it = m_armHandlers.begin(); + it != m_armHandlers.end(); + ++it ) + { + debugOutput(DEBUG_LEVEL_VERBOSE, "Unregistering ARM handler for 0x%016llX\n", (*it)->getStart()); + int err=raw1394_arm_unregister(m_resetHandle, (*it)->getStart()); + if (err) { + debugError(" Failed to unregister ARM handler for 0x%016llX\n", (*it)->getStart()); + debugError(" Error: %s\n", strerror(errno)); + } + } + + delete m_pWatchdog; + if ( m_handle ) { + raw1394_destroy_handle( m_handle ); + } + delete m_handle_lock; + + if ( m_resetHandle ) { + raw1394_destroy_handle( m_resetHandle ); + } + delete m_RHThread_lock; + if ( m_util_handle ) { + raw1394_destroy_handle( m_util_handle ); + } +} + +int +Ieee1394Service::detectNbPorts() +{ + raw1394handle_t tmp_handle = raw1394_new_handle(); + if ( tmp_handle == NULL ) { + debugError("Could not get libraw1394 handle.\n"); + return -1; + } + struct raw1394_portinfo pinf[IEEE1394SERVICE_MAX_FIREWIRE_PORTS]; + int nb_detected_ports = raw1394_get_port_info(tmp_handle, pinf, IEEE1394SERVICE_MAX_FIREWIRE_PORTS); + raw1394_destroy_handle(tmp_handle); + + if (nb_detected_ports < 0) { + debugError("Failed to detect number of ports\n"); + return -1; + } + return nb_detected_ports; +} + +void +Ieee1394Service::doBusReset() { + debugOutput(DEBUG_LEVEL_VERBOSE, "Issue bus reset on service %p (port %d).\n", this, getPort()); + raw1394_reset_bus(m_handle); +} + +bool +Ieee1394Service::initialize( int port ) +{ + using namespace std; + + int nb_ports = detectNbPorts(); + if (port + 1 > nb_ports) { + debugFatal("Requested port (%d) out of range (# ports: %d)\n", port, nb_ports); + } + + if(!m_pWatchdog) { + debugError("No valid RT watchdog found.\n"); + return false; + } + if(!m_pWatchdog->start()) { + debugError("Could not start RT watchdog.\n"); + return false; + } + + m_handle = raw1394_new_handle_on_port( port ); + if ( !m_handle ) { + if ( !errno ) { + debugFatal("libraw1394 not compatible\n"); + } else { + debugFatal("Ieee1394Service::initialize: Could not get 1394 handle: %s\n", + strerror(errno) ); + debugFatal("Is ieee1394 and raw1394 driver loaded?\n"); + } + return false; + } + + m_resetHandle = raw1394_new_handle_on_port( port ); + if ( !m_resetHandle ) { + if ( !errno ) { + debugFatal("libraw1394 not compatible\n"); + } else { + debugFatal("Ieee1394Service::initialize: Could not get 1394 handle: %s", + strerror(errno) ); + debugFatal("Is ieee1394 and raw1394 driver loaded?\n"); + } + return false; + } + + m_util_handle = raw1394_new_handle_on_port( port ); + if ( !m_util_handle ) { + if ( !errno ) { + debugFatal("libraw1394 not compatible\n"); + } else { + debugFatal("Ieee1394Service::initialize: Could not get 1394 handle: %s", + strerror(errno) ); + debugFatal("Is ieee1394 and raw1394 driver loaded?\n"); + } + return false; + } + + // test the cycle timer read function + int err; + uint32_t cycle_timer; + uint64_t local_time; + err=raw1394_read_cycle_timer(m_handle, &cycle_timer, &local_time); + if(err) { + debugOutput(DEBUG_LEVEL_VERBOSE, "raw1394_read_cycle_timer failed.\n"); + debugOutput(DEBUG_LEVEL_VERBOSE, " Error descr: %s\n", strerror(err)); + debugWarning("==================================================================\n"); + debugWarning(" This system doesn't support the raw1394_read_cycle_timer call. \n"); + debugWarning(" Fallback to indirect CTR read method. \n"); + debugWarning(" FFADO should work, but achieving low-latency might be a problem. \n"); + debugWarning(" Upgrade the kernel to version 2.6.21 or higher to solve this. \n"); + debugWarning("==================================================================\n"); + m_have_new_ctr_read = false; + } else { + debugOutput(DEBUG_LEVEL_VERBOSE, "This system supports the raw1394_read_cycle_timer call, using it.\n"); + m_have_new_ctr_read = true; + } + + m_port = port; + + // obtain port name + raw1394handle_t tmp_handle = raw1394_new_handle(); + if ( tmp_handle == NULL ) { + debugError("Could not get temporaty libraw1394 handle.\n"); + return false; + } + struct raw1394_portinfo pinf[IEEE1394SERVICE_MAX_FIREWIRE_PORTS]; + int nb_detected_ports = raw1394_get_port_info(tmp_handle, pinf, IEEE1394SERVICE_MAX_FIREWIRE_PORTS); + raw1394_destroy_handle(tmp_handle); + + if (nb_detected_ports < 0) { + debugError("Failed to detect number of ports\n"); + return false; + } + + if(nb_detected_ports && port < IEEE1394SERVICE_MAX_FIREWIRE_PORTS) { + m_portName = pinf[port].name; + } else { + m_portName = "Unknown"; + } + if (m_portName == "") { + m_portName = "Unknown"; + } + + raw1394_set_userdata( m_handle, this ); + raw1394_set_userdata( m_resetHandle, this ); + raw1394_set_userdata( m_util_handle, this ); + raw1394_set_bus_reset_handler( m_resetHandle, + this->resetHandlerLowLevel ); + + m_default_arm_handler = raw1394_set_arm_tag_handler( m_resetHandle, + this->armHandlerLowLevel ); + + if(!m_pCTRHelper) { + debugFatal("No CycleTimerHelper available, bad!\n"); + return false; + } + m_pCTRHelper->setVerboseLevel(getDebugLevel()); + if(!m_pCTRHelper->Start()) { + debugFatal("Could not start CycleTimerHelper\n"); + return false; + } + + if(!m_pIsoManager) { + debugFatal("No IsoHandlerManager available, bad!\n"); + return false; + } + m_pIsoManager->setVerboseLevel(getDebugLevel()); + if(!m_pIsoManager->init()) { + debugFatal("Could not initialize IsoHandlerManager\n"); + return false; + } + + startRHThread(); + + // make sure that the thread parameters of all our helper threads are OK + if(!setThreadParameters(m_realtime, m_base_priority)) { + debugFatal("Could not set thread parameters\n"); + return false; + } + return true; +} + +bool +Ieee1394Service::setThreadParameters(bool rt, int priority) { + bool result = true; + if (priority > THREAD_MAX_RTPRIO) priority = THREAD_MAX_RTPRIO; + m_base_priority = priority; + m_realtime = rt; + if (m_pIsoManager) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Switching IsoManager to (rt=%d, prio=%d)\n", + rt, priority); + result &= m_pIsoManager->setThreadParameters(rt, priority); + } + if (m_pCTRHelper) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Switching CycleTimerHelper to (rt=%d, prio=%d)\n", + rt && IEEE1394SERVICE_CYCLETIMER_HELPER_RUN_REALTIME, + priority + IEEE1394SERVICE_CYCLETIMER_HELPER_PRIO_INCREASE); + result &= m_pCTRHelper->setThreadParameters(rt && IEEE1394SERVICE_CYCLETIMER_HELPER_RUN_REALTIME, + priority + IEEE1394SERVICE_CYCLETIMER_HELPER_PRIO_INCREASE); + } + return result; +} + +int +Ieee1394Service::getNodeCount() +{ + Util::MutexLockHelper lock(*m_handle_lock); + return raw1394_get_nodecount( m_handle ); +} + +nodeid_t Ieee1394Service::getLocalNodeId() { + Util::MutexLockHelper lock(*m_handle_lock); + return raw1394_get_local_id(m_handle) & 0x3F; +} + +/** + * Returns the current value of the cycle timer (in ticks) + * + * @return the current value of the cycle timer (in ticks) + */ + +uint32_t +Ieee1394Service::getCycleTimerTicks() { + return m_pCTRHelper->getCycleTimerTicks(); +} + +/** + * Returns the current value of the cycle timer (as is) + * + * @return the current value of the cycle timer (as is) + */ +uint32_t +Ieee1394Service::getCycleTimer() { + return m_pCTRHelper->getCycleTimer(); +} + +/** + * Returns the current value of the cycle timer (in ticks) + * for a specific time instant (usecs since epoch) + * @return the current value of the cycle timer (in ticks) + */ + +uint32_t +Ieee1394Service::getCycleTimerTicks(uint64_t t) { + return m_pCTRHelper->getCycleTimerTicks(t); +} + +/** + * Returns the current value of the cycle timer (as is) + * for a specific time instant (usecs since epoch) + * @return the current value of the cycle timer (as is) + */ +uint32_t +Ieee1394Service::getCycleTimer(uint64_t t) { + return m_pCTRHelper->getCycleTimer(t); +} + +bool +Ieee1394Service::readCycleTimerReg(uint32_t *cycle_timer, uint64_t *local_time) +{ + if(m_have_new_ctr_read) { + int err; + err = raw1394_read_cycle_timer(m_util_handle, cycle_timer, local_time); + if(err) { + debugWarning("raw1394_read_cycle_timer: %s\n", strerror(err)); + return false; + } + return true; + } else { + // do a normal read of the CTR register + // the disadvantage is that local_time and cycle time are not + // read at the same time instant (scheduling issues) + *local_time = getCurrentTimeAsUsecs(); + if ( raw1394_read( m_util_handle, + getLocalNodeId() | 0xFFC0, + CSR_REGISTER_BASE | CSR_CYCLE_TIME, + sizeof(uint32_t), cycle_timer ) == 0 ) { + *cycle_timer = CondSwapFromBus32(*cycle_timer); + return true; + } else { + return false; + } + } +} + +uint64_t +Ieee1394Service::getCurrentTimeAsUsecs() { + return Util::SystemTimeSource::getCurrentTimeAsUsecs(); +} + +bool +Ieee1394Service::read( fb_nodeid_t nodeId, + fb_nodeaddr_t addr, + size_t length, + fb_quadlet_t* buffer ) +{ + Util::MutexLockHelper lock(*m_handle_lock); + using namespace std; + if ( raw1394_read( m_handle, nodeId, addr, length*4, buffer ) == 0 ) { + + #ifdef DEBUG + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, + "read: node 0x%hX, addr = 0x%016llX, length = %u\n", + nodeId, addr, length); + printBuffer( DEBUG_LEVEL_VERY_VERBOSE, length, buffer ); + #endif + + return true; + } else { + #ifdef DEBUG + debugOutput(DEBUG_LEVEL_NORMAL, + "raw1394_read failed: node 0x%hX, addr = 0x%016llX, length = %u\n", + nodeId, addr, length); + #endif + return false; + } +} + +bool +Ieee1394Service::read_quadlet( fb_nodeid_t nodeId, + fb_nodeaddr_t addr, + fb_quadlet_t* buffer ) +{ + return read( nodeId, addr, sizeof( *buffer )/4, buffer ); +} + +bool +Ieee1394Service::read_octlet( fb_nodeid_t nodeId, + fb_nodeaddr_t addr, + fb_octlet_t* buffer ) +{ + return read( nodeId, addr, sizeof( *buffer )/4, + reinterpret_cast( buffer ) ); +} + +bool +Ieee1394Service::write( fb_nodeid_t nodeId, + fb_nodeaddr_t addr, + size_t length, + fb_quadlet_t* data ) +{ + Util::MutexLockHelper lock(*m_handle_lock); + using namespace std; + + #ifdef DEBUG + debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"write: node 0x%hX, addr = 0x%016llX, length = %d\n", + nodeId, addr, length); + printBuffer( DEBUG_LEVEL_VERY_VERBOSE, length, data ); + #endif + + return raw1394_write( m_handle, nodeId, addr, length*4, data ) == 0; +} + +bool +Ieee1394Service::write_quadlet( fb_nodeid_t nodeId, + fb_nodeaddr_t addr, + fb_quadlet_t data ) +{ + return write( nodeId, addr, sizeof( data )/4, &data ); +} + +bool +Ieee1394Service::write_octlet( fb_nodeid_t nodeId, + fb_nodeaddr_t addr, + fb_octlet_t data ) +{ + return write( nodeId, addr, sizeof( data )/4, + reinterpret_cast( &data ) ); +} + +bool +Ieee1394Service::lockCompareSwap64( fb_nodeid_t nodeId, + fb_nodeaddr_t addr, + fb_octlet_t compare_value, + fb_octlet_t swap_value, + fb_octlet_t* result ) +{ + #ifdef DEBUG + debugOutput(DEBUG_LEVEL_VERBOSE,"lockCompareSwap64: node 0x%X, addr = 0x%016llX\n", + nodeId, addr); + debugOutput(DEBUG_LEVEL_VERBOSE," if (*(addr)==0x%016llX) *(addr)=0x%016llX\n", + compare_value, swap_value); + fb_octlet_t buffer; + if(!read_octlet( nodeId, addr,&buffer )) { + debugWarning("Could not read register\n"); + } else { + debugOutput(DEBUG_LEVEL_VERBOSE,"before = 0x%016llX\n", buffer); + } + #endif + + // do endiannes swapping + compare_value = CondSwapToBus64(compare_value); + swap_value = CondSwapToBus64(swap_value); + + // do separate locking here (no MutexLockHelper) since + // we use read_octlet in the DEBUG code in this function + m_handle_lock->Lock(); + int retval=raw1394_lock64(m_handle, nodeId, addr, + RAW1394_EXTCODE_COMPARE_SWAP, + swap_value, compare_value, result); + m_handle_lock->Unlock(); + + if(retval) { + debugError("raw1394_lock64 failed: %s\n", strerror(errno)); + } + + #ifdef DEBUG + if(!read_octlet( nodeId, addr,&buffer )) { + debugWarning("Could not read register\n"); + } else { + debugOutput(DEBUG_LEVEL_VERBOSE,"after = 0x%016llX\n", buffer); + } + #endif + + *result = CondSwapFromBus64(*result); + + return (retval == 0); +} + +fb_quadlet_t* +Ieee1394Service::transactionBlock( fb_nodeid_t nodeId, + fb_quadlet_t* buf, + int len, + unsigned int* resp_len ) +{ + // FIXME: this requires transactionBlockClose to unlock + m_handle_lock->Lock(); + for (int i = 0; i < len; ++i) { + buf[i] = CondSwapFromBus32( buf[i] ); + } + + fb_quadlet_t* result = + avc1394_transaction_block2( m_handle, + nodeId, + buf, + len, + resp_len, + 10 ); + + for ( unsigned int i = 0; i < *resp_len; ++i ) { + result[i] = CondSwapToBus32( result[i] ); + } + + return result; +} + + +bool +Ieee1394Service::transactionBlockClose() +{ + avc1394_transaction_block_close( m_handle ); + m_handle_lock->Unlock(); + return true; +} + +int +Ieee1394Service::getVerboseLevel() +{ + return getDebugLevel(); +} + +void +Ieee1394Service::printBuffer( unsigned int level, size_t length, fb_quadlet_t* buffer ) const +{ + + for ( unsigned int i=0; i < length; ++i ) { + if ( ( i % 4 ) == 0 ) { + if ( i > 0 ) { + debugOutputShort(level,"\n"); + } + debugOutputShort(level," %4d: ",i*4); + } + debugOutputShort(level,"%08X ",buffer[i]); + } + debugOutputShort(level,"\n"); +} +void +Ieee1394Service::printBufferBytes( unsigned int level, size_t length, byte_t* buffer ) const +{ + + for ( unsigned int i=0; i < length; ++i ) { + if ( ( i % 16 ) == 0 ) { + if ( i > 0 ) { + debugOutputShort(level,"\n"); + } + debugOutputShort(level," %4d: ",i*16); + } + debugOutputShort(level,"%02X ",buffer[i]); + } + debugOutputShort(level,"\n"); +} + +int +Ieee1394Service::resetHandlerLowLevel( raw1394handle_t handle, + unsigned int generation ) +{ + raw1394_update_generation ( handle, generation ); + Ieee1394Service* instance + = (Ieee1394Service*) raw1394_get_userdata( handle ); + instance->resetHandler( generation ); + + return 0; +} + +bool +Ieee1394Service::resetHandler( unsigned int generation ) +{ + quadlet_t buf=0; + + // do a simple read on ourself in order to update the internal structures + // this avoids failures after a bus reset + read_quadlet( getLocalNodeId() | 0xFFC0, + CSR_REGISTER_BASE | CSR_CYCLE_TIME, + &buf ); + + for ( reset_handler_vec_t::iterator it = m_busResetHandlers.begin(); + it != m_busResetHandlers.end(); + ++it ) + { + Util::Functor* func = *it; + ( *func )(); + } + + return true; +} + +bool Ieee1394Service::registerARMHandler(ARMHandler *h) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Registering ARM handler (%p) for 0x%016llX, length %u\n", + h, h->getStart(), h->getLength()); + + int err=raw1394_arm_register(m_resetHandle, h->getStart(), + h->getLength(), h->getBuffer(), (octlet_t)h, + h->getAccessRights(), + h->getNotificationOptions(), + h->getClientTransactions()); + if (err) { + debugError("Failed to register ARM handler for 0x%016llX\n", h->getStart()); + debugError(" Error: %s\n", strerror(errno)); + return false; + } + + m_armHandlers.push_back( h ); + + return true; +} + +bool Ieee1394Service::unregisterARMHandler( ARMHandler *h ) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Unregistering ARM handler (%p) for 0x%016llX\n", + h, h->getStart()); + + for ( arm_handler_vec_t::iterator it = m_armHandlers.begin(); + it != m_armHandlers.end(); + ++it ) + { + if((*it) == h) { + int err=raw1394_arm_unregister(m_resetHandle, h->getStart()); + if (err) { + debugError("Failed to unregister ARM handler (%p)\n", h); + debugError(" Error: %s\n", strerror(errno)); + } else { + m_armHandlers.erase(it); + return true; + } + } + } + debugOutput(DEBUG_LEVEL_VERBOSE, " handler not found!\n"); + + return false; +} +/** + * @brief Tries to find a free ARM address range + * + * @param start address to start with + * @param length length of the block needed (bytes) + * @param step step to use when searching (bytes) + * @return The base address that is free, and 0xFFFFFFFFFFFFFFFF when failed + */ +nodeaddr_t Ieee1394Service::findFreeARMBlock( nodeaddr_t start, size_t length, size_t step ) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Finding free ARM block of %d bytes, from 0x%016llX in steps of %d bytes\n", + length, start, step); + + int cnt=0; + const int maxcnt=10; + int err=1; + while(err && cnt++ < maxcnt) { + // try to register + err=raw1394_arm_register(m_resetHandle, start, length, 0, 0, 0, 0, 0); + + if (err) { + debugOutput(DEBUG_LEVEL_VERBOSE, " -> cannot use 0x%016llX\n", start); + debugError(" Error: %s\n", strerror(errno)); + start += step; + } else { + debugOutput(DEBUG_LEVEL_VERBOSE, " -> use 0x%016llX\n", start); + err=raw1394_arm_unregister(m_resetHandle, start); + if (err) { + debugOutput(DEBUG_LEVEL_VERBOSE, " error unregistering test handler\n"); + debugError(" Error: %s\n", strerror(errno)); + return 0xFFFFFFFFFFFFFFFFLLU; + } + return start; + } + } + debugOutput(DEBUG_LEVEL_VERBOSE, " Could not find free block in %d tries\n",cnt); + return 0xFFFFFFFFFFFFFFFFLLU; +} + +int +Ieee1394Service::armHandlerLowLevel(raw1394handle_t handle, + unsigned long arm_tag, + byte_t request_type, unsigned int requested_length, + void *data) +{ + Ieee1394Service* instance + = (Ieee1394Service*) raw1394_get_userdata( handle ); + instance->armHandler( arm_tag, request_type, requested_length, data ); + + return 0; +} + +bool +Ieee1394Service::armHandler( unsigned long arm_tag, + byte_t request_type, unsigned int requested_length, + void *data) +{ + for ( arm_handler_vec_t::iterator it = m_armHandlers.begin(); + it != m_armHandlers.end(); + ++it ) + { + if((*it) == (ARMHandler *)arm_tag) { + struct raw1394_arm_request_response *arm_req_resp; + arm_req_resp = (struct raw1394_arm_request_response *) data; + raw1394_arm_request_t arm_req=arm_req_resp->request; + raw1394_arm_response_t arm_resp=arm_req_resp->response; + + debugOutput(DEBUG_LEVEL_VERBOSE,"ARM handler for address 0x%016llX called\n", + (*it)->getStart()); + debugOutput(DEBUG_LEVEL_VERBOSE," request type : 0x%02X\n",request_type); + debugOutput(DEBUG_LEVEL_VERBOSE," request length : %04d\n",requested_length); + + switch(request_type) { + case RAW1394_ARM_READ: + (*it)->handleRead(arm_req); + *arm_resp=*((*it)->getResponse()); + break; + case RAW1394_ARM_WRITE: + (*it)->handleWrite(arm_req); + *arm_resp=*((*it)->getResponse()); + break; + case RAW1394_ARM_LOCK: + (*it)->handleLock(arm_req); + *arm_resp=*((*it)->getResponse()); + break; + default: + debugWarning("Unknown request type received, ignoring...\n"); + } + + return true; + } + } + + debugOutput(DEBUG_LEVEL_VERBOSE,"default ARM handler called\n"); + + m_default_arm_handler(m_resetHandle, arm_tag, request_type, requested_length, data ); + return true; +} + +bool +Ieee1394Service::startRHThread() +{ + int i; + + if ( m_threadRunning ) { + return true; + } + m_RHThread_lock->Lock(); + i = pthread_create( &m_thread, 0, rHThread, this ); + m_RHThread_lock->Unlock(); + if (i) { + debugFatal("Could not start ieee1394 service thread\n"); + return false; + } + m_threadRunning = true; + + return true; +} + +void +Ieee1394Service::stopRHThread() +{ + if ( m_threadRunning ) { + m_RHThread_lock->Lock(); + pthread_cancel (m_thread); + pthread_join (m_thread, 0); + m_RHThread_lock->Unlock(); + m_threadRunning = false; + } +} + +void* +Ieee1394Service::rHThread( void* arg ) +{ + Ieee1394Service* pIeee1394Service = (Ieee1394Service*) arg; + + while (true) { + raw1394_loop_iterate (pIeee1394Service->m_resetHandle); + pthread_testcancel (); + } + + return 0; +} + +bool +Ieee1394Service::addBusResetHandler( Util::Functor* functor ) +{ + debugOutput(DEBUG_LEVEL_VERBOSE, "Adding busreset handler (%p)\n", functor); + m_busResetHandlers.push_back( functor ); + return true; +} + +bool +Ieee1394Service::remBusResetHandler( Util::Functor* functor ) +{ + debugOutput(DEBUG_LEVEL_VERBOSE, "Removing busreset handler (%p)\n", functor); + + for ( reset_handler_vec_t::iterator it = m_busResetHandlers.begin(); + it != m_busResetHandlers.end(); + ++it ) + { + if ( *it == functor ) { + debugOutput(DEBUG_LEVEL_VERBOSE, " found\n"); + m_busResetHandlers.erase( it ); + return true; + } + } + debugOutput(DEBUG_LEVEL_VERBOSE, " not found\n"); + return false; +} + +/** + * Allocates an iso channel for use by the interface in a similar way to + * libiec61883. Returns -1 on error (due to there being no free channels) + * or an allocated channel number. + * + * Does not perform anything other than registering the channel and the + * bandwidth at the IRM + * + * Also allocates the necessary bandwidth (in ISO allocation units). + * + * FIXME: As in libiec61883, channel 63 is not requested; this is either a + * bug or it's omitted since that's the channel preferred by video devices. + * + * @param bandwidth the bandwidth to allocate for this channel + * @return the channel number + */ +signed int Ieee1394Service::allocateIsoChannelGeneric(unsigned int bandwidth) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Allocating ISO channel using generic method...\n" ); + + Util::MutexLockHelper lock(*m_handle_lock); + struct ChannelInfo cinfo; + + int c = -1; + for (c = 0; c < 63; c++) { + if (raw1394_channel_modify (m_handle, c, RAW1394_MODIFY_ALLOC) == 0) + break; + } + if (c < 63) { + if (raw1394_bandwidth_modify(m_handle, bandwidth, RAW1394_MODIFY_ALLOC) < 0) { + debugFatal("Could not allocate bandwidth of %d\n", bandwidth); + + raw1394_channel_modify (m_handle, c, RAW1394_MODIFY_FREE); + return -1; + } else { + cinfo.channel=c; + cinfo.bandwidth=bandwidth; + cinfo.alloctype=AllocGeneric; + + cinfo.xmit_node=-1; + cinfo.xmit_plug=-1; + cinfo.recv_node=-1; + cinfo.recv_plug=-1; + + if (registerIsoChannel(c, cinfo)) { + return c; + } else { + raw1394_bandwidth_modify(m_handle, bandwidth, RAW1394_MODIFY_FREE); + raw1394_channel_modify (m_handle, c, RAW1394_MODIFY_FREE); + return -1; + } + } + } + return -1; +} + +/** + * Allocates an iso channel for use by the interface in a similar way to + * libiec61883. Returns -1 on error (due to there being no free channels) + * or an allocated channel number. + * + * Uses IEC61883 Connection Management Procedure to establish the connection. + * + * Also allocates the necessary bandwidth (in ISO allocation units). + * + * @param xmit_node node id of the transmitter + * @param xmit_plug the output plug to use. If -1, find the first online plug, and + * upon return, contains the plug number used. + * @param recv_node node id of the receiver + * @param recv_plug the input plug to use. If -1, find the first online plug, and + * upon return, contains the plug number used. + * + * @return the channel number + */ + +signed int Ieee1394Service::allocateIsoChannelCMP( + nodeid_t xmit_node, int xmit_plug, + nodeid_t recv_node, int recv_plug + ) { + + debugOutput(DEBUG_LEVEL_VERBOSE, "Allocating ISO channel using IEC61883 CMP...\n" ); + Util::MutexLockHelper lock(*m_handle_lock); + + struct ChannelInfo cinfo; + + int c = -1; + int bandwidth=1; + #if IEEE1394SERVICE_SKIP_IEC61883_BANDWIDTH_ALLOCATION + bandwidth=0; + #endif + + // do connection management: make connection + c = iec61883_cmp_connect( + m_handle, + xmit_node | 0xffc0, + &xmit_plug, + recv_node | 0xffc0, + &recv_plug, + &bandwidth); + + if((c<0) || (c>63)) { + debugError("Could not do CMP from %04X:%02d to %04X:%02d\n", + xmit_node, xmit_plug, recv_node, recv_plug + ); + return -1; + } + + cinfo.channel=c; + cinfo.bandwidth=bandwidth; + cinfo.alloctype=AllocCMP; + + cinfo.xmit_node=xmit_node; + cinfo.xmit_plug=xmit_plug; + cinfo.recv_node=recv_node; + cinfo.recv_plug=recv_plug; + + if (registerIsoChannel(c, cinfo)) { + return c; + } + + return -1; +} + +/** + * Deallocates an iso channel. Silently ignores a request to deallocate + * a negative channel number. + * + * Figures out the method that was used to allocate the channel (generic, cmp, ...) + * and uses the appropriate method to deallocate. Also frees the bandwidth + * that was reserved along with this channel. + * + * @param c channel number + * @return true if successful + */ +bool Ieee1394Service::freeIsoChannel(signed int c) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Freeing ISO channel %d...\n", c ); + Util::MutexLockHelper lock(*m_handle_lock); + + if (c < 0 || c > 63) { + debugWarning("Invalid channel number: %d\n", c); + return false; + } + + switch (m_channels[c].alloctype) { + default: + debugError(" BUG: invalid allocation type!\n"); + return false; + + case AllocFree: + debugWarning(" Channel %d not registered\n", c); + return false; + + case AllocGeneric: + debugOutput(DEBUG_LEVEL_VERBOSE, " allocated using generic routine...\n" ); + debugOutput(DEBUG_LEVEL_VERBOSE, " freeing %d bandwidth units...\n", m_channels[c].bandwidth ); + if (raw1394_bandwidth_modify(m_handle, m_channels[c].bandwidth, RAW1394_MODIFY_FREE) !=0) { + debugWarning("Failed to deallocate bandwidth\n"); + } + debugOutput(DEBUG_LEVEL_VERBOSE, " freeing channel %d...\n", m_channels[c].channel ); + if (raw1394_channel_modify (m_handle, m_channels[c].channel, RAW1394_MODIFY_FREE) != 0) { + debugWarning("Failed to free channel\n"); + } + if (!unregisterIsoChannel(c)) + return false; + return true; + + case AllocCMP: + debugOutput(DEBUG_LEVEL_VERBOSE, " allocated using IEC61883 CMP...\n" ); + debugOutput(DEBUG_LEVEL_VERBOSE, " performing IEC61883 CMP disconnect...\n" ); + if(iec61883_cmp_disconnect( + m_handle, + m_channels[c].xmit_node | 0xffc0, + m_channels[c].xmit_plug, + m_channels[c].recv_node | 0xffc0, + m_channels[c].recv_plug, + m_channels[c].channel, + m_channels[c].bandwidth) != 0) { + debugWarning("Could not do CMP disconnect for channel %d!\n",c); + } + if (!unregisterIsoChannel(c)) + return false; + return true; + } + + // unreachable + debugError("BUG: unreachable code reached!\n"); + + return false; +} + +/** + * Registers a channel as managed by this ieee1394service + * @param c channel number + * @param cinfo channel info struct + * @return true if successful + */ +bool Ieee1394Service::registerIsoChannel(unsigned int c, struct ChannelInfo cinfo) { + if (c < 63) { + if (m_channels[c].alloctype != AllocFree) { + debugWarning("Channel %d already registered with bandwidth %d\n", + m_channels[c].channel, m_channels[c].bandwidth); + } + + memcpy(&m_channels[c], &cinfo, sizeof(struct ChannelInfo)); + + } else return false; + return true; +} + +/** + * unegisters a channel from this ieee1394service + * @param c channel number + * @return true if successful + */ +bool Ieee1394Service::unregisterIsoChannel(unsigned int c) { + if (c < 63) { + if (m_channels[c].alloctype == AllocFree) { + debugWarning("Channel %d not registered\n", c); + return false; + } + + m_channels[c].channel=-1; + m_channels[c].bandwidth=-1; + m_channels[c].alloctype=AllocFree; + m_channels[c].xmit_node=0xFFFF; + m_channels[c].xmit_plug=-1; + m_channels[c].recv_node=0xFFFF; + m_channels[c].recv_plug=-1; + + } else return false; + return true; +} + +/** + * Returns the current value of the `bandwidth available' register on + * the IRM, or -1 on error. + * @return + */ +signed int Ieee1394Service::getAvailableBandwidth() { + quadlet_t buffer; + Util::MutexLockHelper lock(*m_handle_lock); + signed int result = raw1394_read (m_handle, raw1394_get_irm_id (m_handle), + CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE, + sizeof (quadlet_t), &buffer); + + if (result < 0) + return -1; + return CondSwapFromBus32(buffer); +} + +void +Ieee1394Service::setVerboseLevel(int l) +{ + if (m_pIsoManager) m_pIsoManager->setVerboseLevel(l); + if (m_pCTRHelper) m_pCTRHelper->setVerboseLevel(l); + if (m_pWatchdog) m_pWatchdog->setVerboseLevel(l); + setDebugLevel(l); + debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); +} + +void +Ieee1394Service::show() +{ + #ifdef DEBUG + uint32_t cycle_timer; + uint64_t local_time; + if(!readCycleTimerReg(&cycle_timer, &local_time)) { + debugWarning("Could not read cycle timer register\n"); + + } + uint64_t ctr = CYCLE_TIMER_TO_TICKS( cycle_timer ); + + debugOutput( DEBUG_LEVEL_VERBOSE, "Port: %d\n", getPort() ); + debugOutput( DEBUG_LEVEL_VERBOSE, " Name: %s\n", getPortName().c_str() ); + debugOutput( DEBUG_LEVEL_VERBOSE, " CycleTimerHelper: %p, IsoManager: %p, WatchDog: %p\n", + m_pCTRHelper, m_pIsoManager, m_pWatchdog ); + debugOutput( DEBUG_LEVEL_VERBOSE, " Time: %011llu (%03us %04ucy %04uticks)\n", + ctr, + (unsigned int)TICKS_TO_SECS( ctr ), + (unsigned int)TICKS_TO_CYCLES( ctr ), + (unsigned int)TICKS_TO_OFFSET( ctr ) ); + debugOutputShort( DEBUG_LEVEL_NORMAL, "Iso handler info:\n"); + #endif + if (m_pIsoManager) m_pIsoManager->dumpInfo(); +} Index: /branches/libffado-2.0/src/libieee1394/IEC61883.cpp =================================================================== --- /branches/libffado-2.0/src/libieee1394/IEC61883.cpp (revision 864) +++ /branches/libffado-2.0/src/libieee1394/IEC61883.cpp (revision 864) @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "IEC61883.h" + +IMPL_DEBUG_MODULE( IEC61883, IEC61883, DEBUG_LEVEL_NORMAL ); + +IEC61883::IEC61883() { + +} + +IEC61883::~IEC61883() { + +} Index: /branches/libffado-2.0/src/libieee1394/ARMHandler.cpp =================================================================== --- /branches/libffado-2.0/src/libieee1394/ARMHandler.cpp (revision 1046) +++ /branches/libffado-2.0/src/libieee1394/ARMHandler.cpp (revision 1046) @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "ARMHandler.h" + +IMPL_DEBUG_MODULE( ARMHandler, ARMHandler, DEBUG_LEVEL_NORMAL); +/** + * @param start identifies addressrange + * @param length identifies addressrange length (in bytes) + * @param initial_value pointer to buffer containing (if necessary) initial value + * NULL means undefined + * @param access_rights access-rights for registered addressrange handled + * by kernel-part. Value is one or more binary or of the + * following flags - ARM_READ, ARM_WRITE, ARM_LOCK + * @param notification_options identifies for which type of request you want + * to be notified. Value is one or more binary or of the + * following flags - ARM_READ, ARM_WRITE, ARM_LOCK + * @param client_transactions identifies for which type of request you want + * to handle the request by the client application. + * for those requests no response will be generated, but + * has to be generated by the application. + * Value is one or more binary or of the + * following flags - ARM_READ, ARM_WRITE, ARM_LOCK + * For each bit set here, notification_options and + * access_rights will be ignored. + * + */ +ARMHandler::ARMHandler(nodeaddr_t start, size_t length, + unsigned int access_rights, + unsigned int notification_options, + unsigned int client_transactions + ) + : m_start(start), + m_length(length), + m_access_rights(access_rights), + m_notification_options(notification_options), + m_client_transactions(client_transactions), + m_buffer(0) +{ + m_buffer=(byte_t*)calloc(length, sizeof(byte_t)); + memset(&m_response,0,sizeof(m_response)); +} + +ARMHandler::~ARMHandler() { + if(m_buffer) + delete m_buffer; +} + +bool ARMHandler::handleRead(struct raw1394_arm_request *req) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Read\n"); + printRequest(req); + return true; +} + +bool ARMHandler::handleWrite(struct raw1394_arm_request *req) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Write\n"); + printRequest(req); + return true; +} + +bool ARMHandler::handleLock(struct raw1394_arm_request *req) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Lock\n"); + printRequest(req); + return true; +} + +// typedef struct raw1394_arm_request { +// nodeid_t destination_nodeid; +// nodeid_t source_nodeid; +// nodeaddr_t destination_offset; +// u_int8_t tlabel; +// u_int8_t tcode; +// u_int8_t extended_transaction_code; +// u_int32_t generation; +// arm_length_t buffer_length; +// byte_t *buffer; +// } *raw1394_arm_request_t; +// +// typedef struct raw1394_arm_response { +// int response_code; +// arm_length_t buffer_length; +// byte_t *buffer; +// } *raw1394_arm_response_t; +// +// typedef struct raw1394_arm_request_response { +// struct raw1394_arm_request *request; +// struct raw1394_arm_response *response; +// } *raw1394_arm_request_response_t; + +void ARMHandler::printRequest(struct raw1394_arm_request *arm_req) { + debugOutput(DEBUG_LEVEL_VERBOSE, " request info: \n"); + debugOutput(DEBUG_LEVEL_VERBOSE, " from node 0x%04X to node 0x%04X\n", + arm_req->source_nodeid, arm_req->destination_nodeid); + debugOutput(DEBUG_LEVEL_VERBOSE, " tlabel: 0x%02X, tcode: 0x%02X, extended tcode: 0x%02X\n", + arm_req->tlabel, arm_req->tcode, arm_req->extended_transaction_code); + debugOutput(DEBUG_LEVEL_VERBOSE, " generation: %lu\n", + arm_req->generation); + debugOutput(DEBUG_LEVEL_VERBOSE, " buffer length: %lu\n", + arm_req->buffer_length); + printBufferBytes(DEBUG_LEVEL_VERBOSE, arm_req->buffer_length, arm_req->buffer); +} + +void +ARMHandler::printBufferBytes( unsigned int level, size_t length, byte_t* buffer ) const +{ + + for ( unsigned int i=0; i < length; ++i ) { + if ( ( i % 16 ) == 0 ) { + if ( i > 0 ) { + debugOutputShort(level,"\n"); + } + debugOutputShort(level," %4d: ",i*16); + } + debugOutputShort(level,"%02X ",buffer[i]); + } + debugOutputShort(level,"\n"); +} Index: /branches/libffado-2.0/src/libieee1394/ieee1394service.h =================================================================== --- /branches/libffado-2.0/src/libieee1394/ieee1394service.h (revision 1161) +++ /branches/libffado-2.0/src/libieee1394/ieee1394service.h (revision 1161) @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2005-2008 by Daniel Wagner + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef FFADO_IEEE1394SERVICE_H +#define FFADO_IEEE1394SERVICE_H + +#include "fbtypes.h" +#include "libutil/Functors.h" +#include "libutil/Mutex.h" + +#include "debugmodule/debugmodule.h" + +#include "IEC61883.h" + +#include +#include + +#include +#include + +class ARMHandler; +class IsoHandlerManager; +class CycleTimerHelper; + +namespace Util { + class Watchdog; +} + +class Ieee1394Service : public IEC61883 { +public: + Ieee1394Service(); + Ieee1394Service(bool rt, int prio); + ~Ieee1394Service(); + + bool initialize( int port ); + bool setThreadParameters(bool rt, int priority); + Util::Watchdog *getWatchdog() {return m_pWatchdog;}; + + /** + * @brief get number of ports (firewire adapters) in this machine + * + * @return the number of ports + */ + static int detectNbPorts(); + + /** + * @brief get port (adapter) id + * + * @return get port (adapter) id + */ + int getPort() + { return m_port; } + + /** + * @brief get port (adapter) name + * + * @return get port (adapter) name + */ + std::string getPortName() + { return m_portName; }; + + /** + * @brief get number of nodes on the bus + * + * Since the root node always has + * the highest node ID, this number can be used to determine that ID (it's + * LOCAL_BUS|(count-1)). + * + * @return the number of nodes on the bus to which the port is connected. + * This value can change with every bus reset. + */ + int getNodeCount(); + + /** + * @brief get the node id of the local node + * + * @note does not include the bus part (0xFFC0) + * + * @return the node id of the local node + * This value can change with every bus reset. + */ + nodeid_t getLocalNodeId(); + + /** + * @brief get the most recent cycle timer value (in ticks) + * + * @note Uses the most appropriate method for getting the cycle timer + * which is not necessarily a direct read (could be DLL) + */ + uint32_t getCycleTimerTicks(); + + /** + * @brief get the most recent cycle timer value (in CTR format) + * + * @note Uses the most appropriate method for getting the cycle timer + * which is not necessarily a direct read (could be DLL) + */ + uint32_t getCycleTimer(); + + /** + * @brief get the cycle timer value for a specific time instant (in ticks) + * + * @note Uses the most appropriate method for getting the cycle timer + * which is not necessarily a direct read (could be DLL) + */ + uint32_t getCycleTimerTicks(uint64_t t); + + /** + * @brief get the cycle timer value for a specific time instant (in CTR format) + * + * @note Uses the most appropriate method for getting the cycle timer + * which is not necessarily a direct read (could be DLL) + */ + uint32_t getCycleTimer(uint64_t t); + + /** + * @brief read the cycle timer value from the controller (in CTR format) + * + * @note Uses a direct method to read the value from the controller + * @return true if successful + */ + bool readCycleTimerReg(uint32_t *cycle_timer, uint64_t *local_time); + + /** + * @brief provide the current system time + * @return + */ + uint64_t getCurrentTimeAsUsecs(); + + /** + * @brief send async read request to a node and wait for response. + * + * This does the complete transaction and will return when it's finished. + * + * @param node target node (\todo needs 0xffc0 stuff) + * @param addr address to read from + * @param length amount of data to read in quadlets + * @param buffer pointer to buffer where data will be saved + * + * @return true on success or false on failure (sets errno) + */ + bool read( fb_nodeid_t nodeId, + fb_nodeaddr_t addr, + size_t length, + fb_quadlet_t* buffer ); + + bool read_quadlet( fb_nodeid_t nodeId, + fb_nodeaddr_t addr, + fb_quadlet_t* buffer ); + + bool read_octlet( fb_nodeid_t nodeId, + fb_nodeaddr_t addr, + fb_octlet_t* buffer ); + + /** + * @brief send async write request to a node and wait for response. + * + * This does the complete transaction and will return when it's finished. + * + * @param node target node (\XXX needs 0xffc0 stuff) + * @param addr address to write to + * @param length amount of data to write in quadlets + * @param data pointer to data to be sent + * + * @return true on success or false on failure (sets errno) + */ + bool write( fb_nodeid_t nodeId, + fb_nodeaddr_t addr, + size_t length, + fb_quadlet_t* data ); + + bool write_quadlet( fb_nodeid_t nodeId, + fb_nodeaddr_t addr, + fb_quadlet_t data ); + + bool write_octlet( fb_nodeid_t nodeId, + fb_nodeaddr_t addr, + fb_octlet_t data ); + + /** + * @brief send 64-bit compare-swap lock request and wait for response. + * + * swaps the content of \ref addr with \ref swap_value , but only if + * the content of \ref addr equals \ref compare_with + * + * @note takes care of endiannes + * + * @param nodeId target node ID + * @param addr address within target node address space + * @param compare_with value to compare \ref addr with + * @param swap_value new value to put in \ref addr + * @param result the value (originally) in \ref addr + * + * @return true if succesful, false otherwise + */ + bool lockCompareSwap64( fb_nodeid_t nodeId, + fb_nodeaddr_t addr, + fb_octlet_t compare_value, + fb_octlet_t swap_value, + fb_octlet_t* result ); + + fb_quadlet_t* transactionBlock( fb_nodeid_t nodeId, + fb_quadlet_t* buf, + int len, + unsigned int* resp_len ); + + bool transactionBlockClose(); +// FIXME: private for thread safety !! + raw1394handle_t getHandle() {return m_handle;}; + + int getVerboseLevel(); + + bool addBusResetHandler( Util::Functor* functor ); + bool remBusResetHandler( Util::Functor* functor ); + + void doBusReset(); + + /** + * @brief get the current generation + * + * @return the current generation + **/ + unsigned int getGeneration() { + Util::MutexLockHelper lock(*m_handle_lock); + return raw1394_get_generation( m_handle ); + } + + /** + * @brief register an AddressRangeMapping Handler + * @param h pointer to the handler to register + * + * @return true on success or false on failure + **/ + + bool registerARMHandler( ARMHandler *h ); + + /** + * @brief unregister ARM range + * @param h pointer to the handler to unregister + * @return true if successful, false otherwise + */ + bool unregisterARMHandler( ARMHandler *h ); + + nodeaddr_t findFreeARMBlock( nodeaddr_t start, size_t length, size_t step ); + +// ISO channel stuff +public: + signed int getAvailableBandwidth(); + signed int allocateIsoChannelGeneric(unsigned int bandwidth); + signed int allocateIsoChannelCMP(nodeid_t xmit_node, int xmit_plug, + nodeid_t recv_node, int recv_plug); + bool freeIsoChannel(signed int channel); + + IsoHandlerManager& getIsoHandlerManager() {return *m_pIsoManager;}; +private: + enum EAllocType { + AllocFree = 0, // not allocated (by us) + AllocGeneric = 1, // allocated with generic functions + AllocCMP=2 // allocated with CMP + }; + + struct ChannelInfo { + int channel; + int bandwidth; + enum EAllocType alloctype; + nodeid_t xmit_node; + int xmit_plug; + nodeid_t recv_node; + int recv_plug; + }; + + // the info for the channels we manage + struct ChannelInfo m_channels[64]; + + bool unregisterIsoChannel(unsigned int c); + bool registerIsoChannel(unsigned int c, struct ChannelInfo cinfo); + +private: + + bool startRHThread(); + void stopRHThread(); + static void* rHThread( void* arg ); + + void printBuffer( unsigned int level, size_t length, fb_quadlet_t* buffer ) const; + void printBufferBytes( unsigned int level, size_t length, byte_t* buffer ) const; + + static int resetHandlerLowLevel( raw1394handle_t handle, + unsigned int generation ); + bool resetHandler( unsigned int generation ); + + static int armHandlerLowLevel(raw1394handle_t handle, unsigned long arm_tag, + byte_t request_type, unsigned int requested_length, + void *data); + bool armHandler( unsigned long arm_tag, + byte_t request_type, unsigned int requested_length, + void *data); + + raw1394handle_t m_handle; + Util::Mutex* m_handle_lock; + raw1394handle_t m_resetHandle; + raw1394handle_t m_util_handle; // a handle for operations from the rt thread + int m_port; + std::string m_portName; + + pthread_t m_thread; + Util::Mutex* m_RHThread_lock; + bool m_threadRunning; + + bool m_realtime; + int m_base_priority; + + IsoHandlerManager* m_pIsoManager; + CycleTimerHelper* m_pCTRHelper; + bool m_have_new_ctr_read; + + // the RT watchdog + Util::Watchdog* m_pWatchdog; + + typedef std::vector< Util::Functor* > reset_handler_vec_t; + reset_handler_vec_t m_busResetHandlers; + + // ARM stuff + arm_tag_handler_t m_default_arm_handler; + + typedef std::vector< ARMHandler * > arm_handler_vec_t; + arm_handler_vec_t m_armHandlers; + +public: + void setVerboseLevel(int l); + void show(); +private: + DECLARE_DEBUG_MODULE; +}; + +#endif // FFADO_IEEE1394SERVICE_H Index: /branches/libffado-2.0/src/libieee1394/IEC61883.h =================================================================== --- /branches/libffado-2.0/src/libieee1394/IEC61883.h (revision 864) +++ /branches/libffado-2.0/src/libieee1394/IEC61883.h (revision 864) @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_IEC61883__ +#define __FFADO_IEC61883__ + +#include "../debugmodule/debugmodule.h" + +/* + * This is shamelessly stolen from iec61883-private, + * but I need these functions! + * FIXME: this will only work until somebody decides to change + * these in libiec61883. + */ +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Plug Control Registers + **/ + +/* maximum number of PCRs allowed within the standard + * MPR/PCR addresses defined in IEC-61883. + * This refers to the number of output or input PCRs-- + * not the MPRs and not the combined total. + */ +#define IEC61883_PCR_MAX 31 + +/* standard CSR offsets for plugs */ +#define CSR_O_MPR 0x900 +#define CSR_O_PCR_0 0x904 + +#define CSR_I_MPR 0x980 +#define CSR_I_PCR_0 0x984 + +#if ( __BYTE_ORDER == __BIG_ENDIAN ) + +struct iec61883_oMPR { + unsigned int data_rate:2; + unsigned int bcast_channel:6; + unsigned int non_persist_ext:8; + unsigned int persist_ext:8; + unsigned int reserved:3; + unsigned int n_plugs:5; +}; + +struct iec61883_iMPR { + unsigned int data_rate:2; + unsigned int reserved:6; + unsigned int non_persist_ext:8; + unsigned int persist_ext:8; + unsigned int reserved2:3; + unsigned int n_plugs:5; +}; + +struct iec61883_oPCR { + unsigned int online:1; + unsigned int bcast_connection:1; + unsigned int n_p2p_connections:6; + unsigned int reserved:2; + unsigned int channel:6; + unsigned int data_rate:2; + unsigned int overhead_id:4; + unsigned int payload:10; +}; + +struct iec61883_iPCR { + unsigned int online:1; + unsigned int bcast_connection:1; + unsigned int n_p2p_connections:6; + unsigned int reserved:2; + unsigned int channel:6; + unsigned int reserved2:16; +}; + +#else + +struct iec61883_oMPR { + unsigned int n_plugs:5; + unsigned int reserved:3; + unsigned int persist_ext:8; + unsigned int non_persist_ext:8; + unsigned int bcast_channel:6; + unsigned int data_rate:2; +}; + +struct iec61883_iMPR { + unsigned int n_plugs:5; + unsigned int reserved2:3; + unsigned int persist_ext:8; + unsigned int non_persist_ext:8; + unsigned int reserved:6; + unsigned int data_rate:2; +}; + +struct iec61883_oPCR { + unsigned int payload:10; + unsigned int overhead_id:4; + unsigned int data_rate:2; + unsigned int channel:6; + unsigned int reserved:2; + unsigned int n_p2p_connections:6; + unsigned int bcast_connection:1; + unsigned int online:1; +}; + +struct iec61883_iPCR { + unsigned int reserved2:16; + unsigned int channel:6; + unsigned int reserved:2; + unsigned int n_p2p_connections:6; + unsigned int bcast_connection:1; + unsigned int online:1; +}; + +#endif + +/** + * iec61883_plug_get - Read a node's plug register. + * @h: A raw1394 handle. + * @n: The node id of the node to read + * @a: The CSR offset address (relative to base) of the register to read. + * @value: A pointer to a quadlet where the plug register's value will be stored. + * + * This function handles bus to host endian conversion. It returns 0 for + * suceess or -1 for error (errno available). + **/ +int +iec61883_plug_get(raw1394handle_t h, nodeid_t n, nodeaddr_t a, quadlet_t *value); + + +/** + * iec61883_plug_set - Write a node's plug register. + * @h: A raw1394 handle. + * @n: The node id of the node to read + * @a: The CSR offset address (relative to CSR base) of the register to write. + * @value: A quadlet containing the new register value. + * + * This uses a compare/swap lock operation to safely write the + * new register value, as required by IEC 61883-1. + * This function handles host to bus endian conversion. It returns 0 for success + * or -1 for error (errno available). + **/ +int +iec61883_plug_set(raw1394handle_t h, nodeid_t n, nodeaddr_t a, quadlet_t value); + +/** + * High level plug access macros + */ + +#define iec61883_get_oMPR(h,n,v) iec61883_plug_get((h), (n), CSR_O_MPR, (quadlet_t *)(v)) +#define iec61883_set_oMPR(h,n,v) iec61883_plug_set((h), (n), CSR_O_MPR, *((quadlet_t *)&(v))) +#define iec61883_get_oPCR0(h,n,v) iec61883_plug_get((h), (n), CSR_O_PCR_0, (quadlet_t *)(v)) +#define iec61883_set_oPCR0(h,n,v) iec61883_plug_set((h), (n), CSR_O_PCR_0, *((quadlet_t *)&(v))) +#define iec61883_get_oPCRX(h,n,v,x) iec61883_plug_get((h), (n), CSR_O_PCR_0+(4*(x)), (quadlet_t *)(v)) +#define iec61883_set_oPCRX(h,n,v,x) iec61883_plug_set((h), (n), CSR_O_PCR_0+(4*(x)), *((quadlet_t *)&(v))) +#define iec61883_get_iMPR(h,n,v) iec61883_plug_get((h), (n), CSR_I_MPR, (quadlet_t *)(v)) +#define iec61883_set_iMPR(h,n,v) iec61883_plug_set((h), (n), CSR_I_MPR, *((quadlet_t *)&(v))) +#define iec61883_get_iPCR0(h,n,v) iec61883_plug_get((h), (n), CSR_I_PCR_0, (quadlet_t *)(v)) +#define iec61883_set_iPCR0(h,n,v) iec61883_plug_set((h), (n), CSR_I_PCR_0, *((quadlet_t *)&(v))) +#define iec61883_get_iPCRX(h,n,v,x) iec61883_plug_get((h), (n), CSR_I_PCR_0+(4*(x)), (quadlet_t *)(v)) +#define iec61883_set_iPCRX(h,n,v,x) iec61883_plug_set((h), (n), CSR_I_PCR_0+(4*(x)), *((quadlet_t *)&(v))) + + +#ifdef __cplusplus +} +#endif + +class IEC61883 { + +public: + + IEC61883(); + virtual ~IEC61883(); + +protected: + DECLARE_DEBUG_MODULE; + +}; + +#endif /* __FFADO_IEC61883__ */ + + Index: /branches/libffado-2.0/src/libieee1394/vendor_model_ids.h =================================================================== --- /branches/libffado-2.0/src/libieee1394/vendor_model_ids.h (revision 864) +++ /branches/libffado-2.0/src/libieee1394/vendor_model_ids.h (revision 864) @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef VENDOR_MODEL_IDS +#define VENDOR_MODEL_IDS + +#define FW_VENDORID_TERRATEC 0x000aac +#define FW_VENDORID_MACKIE 0x00000f +#define FW_VENDORID_APOGEE 0x0003db +#define FW_VENDORID_BRIDGECO 0x0007f5 +#define FW_VENDORID_PRESONUS 0x000a92 +#define FW_VENDORID_ESI 0x000f1b +#define FW_VENDORID_FOCUSRITE 0x00130e +#define FW_VENDORID_EDIROL 0x0040ab +#define FW_VENDORID_MAUDIO 0x000d6c +#define FW_VENDORID_ECHO 0x001486 +#define FW_VENDORID_RME 0x000a35 +#define FW_VENDORID_MOTU 0x0001f2 +#define FW_VENDORID_TCAT 0x000166 + +// this is the one we assign ourselves +// maybe once we can get a real one :) +#define FW_VENDORID_FFADO 0x0B0001 + + +#endif /* VENDOR_MODEL_IDS */ Index: /branches/libffado-2.0/src/libieee1394/ARMHandler.h =================================================================== --- /branches/libffado-2.0/src/libieee1394/ARMHandler.h (revision 1137) +++ /branches/libffado-2.0/src/libieee1394/ARMHandler.h (revision 1137) @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * Copyright (C) 2005-2008 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_ARMHANDLER__ +#define __FFADO_ARMHANDLER__ + +#include "../debugmodule/debugmodule.h" + +#include + +#include "ieee1394service.h" + +/** + * @brief Class to handle AddressRangeMappings + * + * This class is intended to help with implementing + * address range mapping, i.e. implementing handlers + * that react to reads/writes of certain addresses + * in 1394 memory space + * + * see the _arm_ functions in raw1394.h for more insight + * + */ + +class ARMHandler { +friend class Ieee1394Service; + +public: + + ARMHandler(nodeaddr_t start, size_t length, + unsigned int access_rights, + unsigned int notification_options, + unsigned int client_transactions + ); + + virtual ~ARMHandler(); + + bool handleRead(struct raw1394_arm_request *); + bool handleWrite(struct raw1394_arm_request *); + bool handleLock(struct raw1394_arm_request *); + + struct raw1394_arm_response *getResponse() {return &m_response;}; + + nodeaddr_t getStart() {return m_start;}; + size_t getLength() {return m_length;}; + unsigned int getAccessRights() {return m_access_rights;}; + unsigned int getNotificationOptions() {return m_notification_options;}; + unsigned int getClientTransactions() {return m_client_transactions;}; + + byte_t *getBuffer() {return m_buffer;}; + +private: + nodeaddr_t m_start; + size_t m_length; + unsigned int m_access_rights; + unsigned int m_notification_options; + unsigned int m_client_transactions; + + byte_t *m_buffer; + + struct raw1394_arm_response m_response; + + void printBufferBytes( unsigned int level, size_t length, byte_t* buffer ) const; + void printRequest(struct raw1394_arm_request *arm_req); + +protected: + + + DECLARE_DEBUG_MODULE; + +}; + +#endif /* __FFADO_ARMHANDLER__ */ + + Index: /branches/libffado-2.0/src/libieee1394/configrom.cpp =================================================================== --- /branches/libffado-2.0/src/libieee1394/configrom.cpp (revision 1186) +++ /branches/libffado-2.0/src/libieee1394/configrom.cpp (revision 1186) @@ -0,0 +1,672 @@ +/* + * Copyright (C) 2005-2008 by Daniel Wagner + * Copyright (C) 2005-2008 by Jonathan Woithe + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "configrom.h" +#include "ieee1394service.h" + +#include "vendor_model_ids.h" + +#include "libutil/SystemTimeSource.h" + +#include +#include + +#include +#include + +using namespace std; + +IMPL_DEBUG_MODULE( ConfigRom, ConfigRom, DEBUG_LEVEL_NORMAL ); + +static int busRead( struct csr1212_csr* csr, + u_int64_t addr, + u_int16_t length, + void* buffer, + void* private_data ); + +static int getMaxRom( u_int32_t* bus_info_data, + void* private_data ); + +static struct csr1212_bus_ops configrom_csr1212_ops = { + busRead, + 0, + 0, + getMaxRom +}; + +struct config_csr_info { + Ieee1394Service* service; + fb_nodeid_t nodeId; +}; + +//------------------------------------------------------------- + +ConfigRom::ConfigRom( Ieee1394Service& ieee1394service, fb_nodeid_t nodeId ) + : Control::Element(NULL, "ConfigRom") + , m_1394Service( ieee1394service ) + , m_nodeId( nodeId ) + , m_avcDevice( false ) // FIXME: this does not seem veryu + , m_guid( 0 ) + , m_vendorName( "" ) + , m_modelName( "" ) + , m_vendorId( 0 ) + , m_modelId( 0 ) + , m_unit_specifier_id( 0 ) + , m_unit_version( 0 ) + , m_isIsoResourceManager( false ) + , m_isCycleMasterCapable( false ) + , m_isSupportIsoOperations( false ) + , m_isBusManagerCapable( false ) + , m_cycleClkAcc( 0 ) + , m_maxRec( 0 ) + , m_nodeVendorId( 0 ) + , m_chipIdHi( 0 ) + , m_chipIdLow( 0 ) + , m_vendorNameKv( 0 ) + , m_modelNameKv( 0 ) + , m_csr( 0 ) +{ +} + +ConfigRom::ConfigRom() + : Control::Element(NULL, "ConfigRom") + , m_1394Service( *(new Ieee1394Service()) ) + , m_nodeId( -1 ) + , m_avcDevice( false ) // FIXME: this does not seem veryu + , m_guid( 0 ) + , m_vendorName( "" ) + , m_modelName( "" ) + , m_vendorId( 0 ) + , m_modelId( 0 ) + , m_unit_specifier_id( 0 ) + , m_unit_version( 0 ) + , m_isIsoResourceManager( false ) + , m_isCycleMasterCapable( false ) + , m_isSupportIsoOperations( false ) + , m_isBusManagerCapable( false ) + , m_cycleClkAcc( 0 ) + , m_maxRec( 0 ) + , m_nodeVendorId( 0 ) + , m_chipIdHi( 0 ) + , m_chipIdLow( 0 ) + , m_vendorNameKv( 0 ) + , m_modelNameKv( 0 ) + , m_csr( 0 ) +{ +} + +Ieee1394Service& +ConfigRom::get1394Service() +{ + return m_1394Service; +} + +bool +ConfigRom::operator == ( const ConfigRom& rhs ) +{ + return m_guid == rhs.m_guid; +} + +bool +ConfigRom::compareGUID( const ConfigRom& a, const ConfigRom& b ) { + return a.getGuid() > b.getGuid(); +} + +bool +ConfigRom::initialize() +{ + struct config_csr_info csr_info; + csr_info.service = &m_1394Service; + csr_info.nodeId = 0xffc0 | m_nodeId; + + m_csr = csr1212_create_csr( &configrom_csr1212_ops, + 5 * sizeof(fb_quadlet_t), // XXX Why 5 ?!? + &csr_info ); + if (!m_csr || csr1212_parse_csr( m_csr ) != CSR1212_SUCCESS) { + debugError( "Could not parse config rom of node %d on port %d\n", m_nodeId, m_1394Service.getPort() ); + if (m_csr) { + csr1212_destroy_csr(m_csr); + m_csr = 0; + } + return false; + } + + // Process Bus_Info_Block + m_isIsoResourceManager = CSR1212_BE32_TO_CPU(m_csr->bus_info_data[2] ) >> 31; + m_isCycleMasterCapable = ( CSR1212_BE32_TO_CPU(m_csr->bus_info_data[2] ) >> 30 ) & 0x1; + m_isSupportIsoOperations = ( CSR1212_BE32_TO_CPU(m_csr->bus_info_data[2] ) >> 29 ) & 0x1; + m_isBusManagerCapable = ( CSR1212_BE32_TO_CPU(m_csr->bus_info_data[2] ) >> 28 ) & 0x1; + m_cycleClkAcc = ( CSR1212_BE32_TO_CPU(m_csr->bus_info_data[2] ) >> 16 ) & 0xff; + m_maxRec = ( CSR1212_BE32_TO_CPU( m_csr->bus_info_data[2] ) >> 12 ) & 0xf; + m_nodeVendorId = ( CSR1212_BE32_TO_CPU( m_csr->bus_info_data[3] ) >> 8 ); + m_chipIdHi = ( CSR1212_BE32_TO_CPU( m_csr->bus_info_data[3] ) ) & 0xff; + m_chipIdLow = CSR1212_BE32_TO_CPU( m_csr->bus_info_data[4] ); + + // Process Root Directory + processRootDirectory(m_csr); + + if ( m_vendorNameKv ) { + int len = ( m_vendorNameKv->value.leaf.len - 2) * sizeof( quadlet_t ); + char* buf = new char[len+2]; + memcpy( buf, + ( void* )CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA( m_vendorNameKv ), + len ); + + while ((buf + len - 1) == '\0') { + len--; + } + // \todo XXX seems a bit strage to do this but the nodemgr.c code does + // it. try to figure out why this is needed (or not) + buf[len++] = ' '; + buf[len] = '\0'; + + + debugOutput( DEBUG_LEVEL_VERBOSE, "Vendor name: '%s'\n", buf ); + m_vendorName = buf; + delete[] buf; + } + if ( m_modelNameKv ) { + int len = ( m_modelNameKv->value.leaf.len - 2) * sizeof( quadlet_t ); + char* buf = new char[len+2]; + memcpy( buf, + ( void* )CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA( m_modelNameKv ), + len ); + while ((buf + len - 1) == '\0') { + len--; + } + // \todo XXX for edirol fa-66 it seems somehow broken. see above + // todo as well. + buf[len++] = ' '; + buf[len] = '\0'; + + debugOutput( DEBUG_LEVEL_VERBOSE, "Model name: '%s'\n", buf); + m_modelName = buf; + delete[] buf; + } + + m_guid = ((u_int64_t)CSR1212_BE32_TO_CPU(m_csr->bus_info_data[3]) << 32) + | CSR1212_BE32_TO_CPU(m_csr->bus_info_data[4]); + + if ( m_vendorNameKv ) { + csr1212_release_keyval( m_vendorNameKv ); + m_vendorNameKv = 0; + } + if ( m_modelNameKv ) { + csr1212_release_keyval( m_modelNameKv ); + m_modelNameKv = 0; + } + if ( m_csr ) { + csr1212_destroy_csr(m_csr); + m_csr = 0; + } + return true; +} + +static int +busRead( struct csr1212_csr* csr, + u_int64_t addr, + u_int16_t length, + void* buffer, + void* private_data ) +{ + struct config_csr_info* csr_info = (struct config_csr_info*) private_data; + + int nb_retries = 5; + + while ( nb_retries-- + && !csr_info->service->read( csr_info->nodeId, + addr, + (size_t)length/4, + ( quadlet_t* )buffer) ) + {// failed, retry + Util::SystemTimeSource::SleepUsecRelative(IEEE1394SERVICE_CONFIGROM_READ_WAIT_USECS); + } + Util::SystemTimeSource::SleepUsecRelative(IEEE1394SERVICE_CONFIGROM_READ_WAIT_USECS); + + if (nb_retries > -1) return 0; // success + else return -1; // failure +} + +static int +getMaxRom( u_int32_t* bus_info_data, + void* /*private_data*/) +{ + return (CSR1212_BE32_TO_CPU( bus_info_data[2] ) >> 8) & 0x3; +} + + +void +ConfigRom::processUnitDirectory( struct csr1212_csr* csr, + struct csr1212_keyval* ud_kv, + unsigned int *id ) +{ + struct csr1212_dentry *dentry; + struct csr1212_keyval *kv; + unsigned int last_key_id = 0; + + debugOutput( DEBUG_LEVEL_VERBOSE, "process unit directory:\n" ); + csr1212_for_each_dir_entry(csr, kv, ud_kv, dentry) { + switch (kv->key.id) { + case CSR1212_KV_ID_VENDOR: + if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) { + debugOutput( DEBUG_LEVEL_VERBOSE, + "\tvendor_id = 0x%08x\n", + kv->value.immediate); + m_vendorId = kv->value.immediate; + } + break; + + case CSR1212_KV_ID_MODEL: + debugOutput( DEBUG_LEVEL_VERBOSE, + "\tmodel_id = 0x%08x\n", + kv->value.immediate); + m_modelId = kv->value.immediate; + break; + + case CSR1212_KV_ID_SPECIFIER_ID: + debugOutput( DEBUG_LEVEL_VERBOSE, + "\tspecifier_id = 0x%08x\n", + kv->value.immediate); + m_unit_specifier_id = kv->value.immediate; + break; + + case CSR1212_KV_ID_VERSION: + debugOutput( DEBUG_LEVEL_VERBOSE, + "\tversion = 0x%08x\n", + kv->value.immediate); + m_unit_version = kv->value.immediate; + if ( m_unit_specifier_id == 0x0000a02d ) // XXX + { + m_avcDevice = true; // FIXME: disable this check for the moment + if ( kv->value.immediate == 0x14001 ) { + m_avcDevice = true; + } + } + break; + + case CSR1212_KV_ID_DESCRIPTOR: + if (kv->key.type == CSR1212_KV_TYPE_LEAF && + CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 && + CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 && + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 && + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 && + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) + { + switch (last_key_id) { + case CSR1212_KV_ID_VENDOR: + csr1212_keep_keyval(kv); + m_vendorNameKv = kv; + break; + + case CSR1212_KV_ID_MODEL: + m_modelNameKv = kv; + csr1212_keep_keyval(kv); + break; + + } + } /* else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) ... */ + break; + + case CSR1212_KV_ID_DEPENDENT_INFO: + if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) { + /* This should really be done in SBP2 as this is + * doing SBP2 specific parsing. */ + processUnitDirectory(csr, kv, id); + } + + break; + + default: + break; + } + last_key_id = kv->key.id; + } +} + +void +ConfigRom::processRootDirectory(struct csr1212_csr* csr) +{ + unsigned int ud_id = 0; + struct csr1212_dentry *dentry; + struct csr1212_keyval *kv; + unsigned int last_key_id = 0; + + csr1212_for_each_dir_entry(csr, kv, csr->root_kv, dentry) { + switch (kv->key.id) { + case CSR1212_KV_ID_VENDOR: + debugOutput( DEBUG_LEVEL_VERBOSE, + "vendor id = 0x%08x\n", kv->value.immediate); + break; + + case CSR1212_KV_ID_NODE_CAPABILITIES: + debugOutput( DEBUG_LEVEL_VERBOSE, + "capabilities = 0x%08x\n", kv->value.immediate); + break; + + case CSR1212_KV_ID_UNIT: + processUnitDirectory(csr, kv, &ud_id); + break; + + case CSR1212_KV_ID_DESCRIPTOR: + if (last_key_id == CSR1212_KV_ID_VENDOR) { + if (kv->key.type == CSR1212_KV_TYPE_LEAF && + CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 && + CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 && + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 && + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 && + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) + { + m_vendorNameKv = kv; + csr1212_keep_keyval(kv); + } + } + break; + } + last_key_id = kv->key.id; + } + +} + +const fb_nodeid_t +ConfigRom::getNodeId() const +{ + return m_nodeId; +} + +const fb_octlet_t +ConfigRom::getGuid() const +{ + return m_guid; +} + +const std::string +ConfigRom::getGuidString() const +{ + char* buf; + asprintf( &buf, "%08x%08x", + ( unsigned int ) ( getGuid() >> 32 ), + ( unsigned int ) ( getGuid() & 0xffffffff ) ); + std::string result = buf; + free( buf ); + return result; +} + +const std::string +ConfigRom::getModelName() const +{ + // HACK: + // workarounds for devices that don't fill a correct model name + switch(m_vendorId) { + case FW_VENDORID_MOTU: + switch(m_unit_specifier_id) { + case 0x00000003: + return "828MkII"; + case 0x00000009: + return "Traveler"; + case 0x0000000d: + return "UltraLite"; + case 0x0000000f: + return "8pre"; + case 0x00000001: + return "828MkI"; + case 0x00000005: + return "896HD"; + default: + return "unknown"; + } + break; + default: + return m_modelName; + } +} + +const std::string +ConfigRom::getVendorName() const +{ + // HACK: + // workarounds for devices that don't fill a correct vendor name + switch(m_vendorId) { + case FW_VENDORID_MOTU: + return "MOTU"; + default: + return m_vendorName; + } +} + +const unsigned int +ConfigRom::getModelId() const +{ + return m_modelId; +} + +const unsigned int +ConfigRom::getVendorId() const +{ + return m_vendorId; +} + +const unsigned int +ConfigRom::getUnitSpecifierId() const +{ + return m_unit_specifier_id; +} + +const unsigned int +ConfigRom::getUnitVersion() const +{ + return m_unit_version; +} + +bool +ConfigRom::updatedNodeId() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, + "Checking for updated node id for device with GUID 0x%016llX...\n", + getGuid()); + + struct csr1212_csr* csr = NULL; + for ( fb_nodeid_t nodeId = 0; + nodeId < m_1394Service.getNodeCount(); + ++nodeId ) + { + struct config_csr_info csr_info; + csr_info.service = &m_1394Service; + csr_info.nodeId = 0xffc0 | nodeId; + debugOutput( DEBUG_LEVEL_VERBOSE, "Looking at node %d...\n", nodeId); + + csr = csr1212_create_csr( &configrom_csr1212_ops, + 5 * sizeof(fb_quadlet_t), // XXX Why 5 ?!? + &csr_info ); + + if (!csr || csr1212_parse_csr( csr ) != CSR1212_SUCCESS) { + debugWarning( "Failed to get/parse CSR\n"); + if (csr) { + csr1212_destroy_csr(csr); + csr = NULL; + } + continue; + } + + octlet_t guid = + ((u_int64_t)CSR1212_BE32_TO_CPU(csr->bus_info_data[3]) << 32) + | CSR1212_BE32_TO_CPU(csr->bus_info_data[4]); + + debugOutput( DEBUG_LEVEL_VERBOSE, + " Node has GUID 0x%016llX\n", + guid); + + if ( guid == getGuid() ) { + debugOutput( DEBUG_LEVEL_VERBOSE, "GUID matches ours\n"); + if ( nodeId != getNodeId() ) { + debugOutput( DEBUG_LEVEL_VERBOSE, + "Device with GUID 0x%016llX changed node id " + "from %d to %d\n", + getGuid(), + getNodeId(), + nodeId ); + m_nodeId = nodeId; + } else { + debugOutput( DEBUG_LEVEL_VERBOSE, + "Device with GUID 0x%016llX kept node id %d\n", + getGuid(), + getNodeId()); + } + if (csr) { + csr1212_destroy_csr(csr); + csr = NULL; + } + return true; + } + } + + if (csr) { + csr1212_destroy_csr(csr); + } + + debugOutput( DEBUG_LEVEL_NORMAL, + "Device with GUID 0x%08x%08x could not be found on " + "the bus anymore (removed?)\n", + m_guid >> 32, + m_guid & 0xffffffff ); + return false; +} + +void +ConfigRom::printConfigRomDebug() const +{ + using namespace std; + debugOutput(DEBUG_LEVEL_NORMAL, "Config ROM\n" ); + debugOutput(DEBUG_LEVEL_NORMAL, "\tCurrent Node Id:\t%d\n", getNodeId() ); + debugOutput(DEBUG_LEVEL_NORMAL, "\tGUID:\t\t\t0x%016llX\n", getGuid()); + debugOutput(DEBUG_LEVEL_NORMAL, "\tVendor Name:\t\t%s\n", getVendorName().c_str() ); + debugOutput(DEBUG_LEVEL_NORMAL, "\tModel Name:\t\t%s\n", getModelName().c_str() ); + debugOutput(DEBUG_LEVEL_NORMAL, "\tNode Vendor ID:\t\t0x%06x\n", getNodeVendorId() ); + debugOutput(DEBUG_LEVEL_NORMAL, "\tModel Id:\t\t0x%08x\n", getModelId() ); + debugOutput(DEBUG_LEVEL_NORMAL, "\tUnit Specifier ID:\t0x%06x\n", getUnitSpecifierId() ); + debugOutput(DEBUG_LEVEL_NORMAL, "\tUnit version:\t\t0x%08x\n", getUnitVersion() ); + debugOutput(DEBUG_LEVEL_NORMAL, "\tISO resource manager:\t%d\n", isIsoResourseManager() ); + debugOutput(DEBUG_LEVEL_NORMAL, "\tCycle master capable:\t%d\n", isSupportsIsoOperations() ); + debugOutput(DEBUG_LEVEL_NORMAL, "\tBus manager capable:\t%d\n", isBusManagerCapable() ); + debugOutput(DEBUG_LEVEL_NORMAL, "\tCycle clock accuracy:\t%d\n", getCycleClockAccurancy() ); + debugOutput(DEBUG_LEVEL_NORMAL, "\tMax rec:\t\t%d (max asy payload: %d bytes)\n", + getMaxRec(), getAsyMaxPayload() ); +} + +void +ConfigRom::printConfigRom() const +{ + using namespace std; + printMessage("Config ROM\n" ); + printMessage("\tCurrent Node Id:\t%d\n", getNodeId() ); + printMessage("\tGUID:\t\t\t0x%016llX\n", getGuid()); + printMessage("\tVendor Name:\t\t%s\n", getVendorName().c_str() ); + printMessage("\tModel Name:\t\t%s\n", getModelName().c_str() ); + printMessage("\tNode Vendor ID:\t\t0x%06x\n", getNodeVendorId() ); + printMessage("\tModel Id:\t\t0x%08x\n", getModelId() ); + printMessage("\tUnit Specifier ID:\t0x%06x\n", getUnitSpecifierId() ); + printMessage("\tUnit version:\t\t0x%08x\n", getUnitVersion() ); + printMessage("\tISO resource manager:\t%d\n", isIsoResourseManager() ); + printMessage("\tCycle master capable:\t%d\n", isSupportsIsoOperations() ); + printMessage("\tBus manager capable:\t%d\n", isBusManagerCapable() ); + printMessage("\tCycle clock accuracy:\t%d\n", getCycleClockAccurancy() ); + printMessage("\tMax rec:\t\t%d (max asy payload: %d bytes)\n", getMaxRec(), getAsyMaxPayload() ); +} + +unsigned short +ConfigRom::getAsyMaxPayload() const +{ + // XXX use pow instead? + return 1 << ( m_maxRec + 1 ); +} + +bool +ConfigRom::serialize( std::string path, Util::IOSerialize& ser ) +{ + bool result; + result = ser.write( path + "m_nodeId", m_nodeId ); + result &= ser.write( path + "m_avcDevice", m_avcDevice ); + result &= ser.write( path + "m_guid", m_guid ); + result &= ser.write( path + "m_vendorName", std::string( m_vendorName ) ); + result &= ser.write( path + "m_modelName", std::string( m_modelName ) ); + result &= ser.write( path + "m_vendorId", m_vendorId ); + result &= ser.write( path + "m_modelId", m_modelId ); + result &= ser.write( path + "m_unit_specifier_id", m_unit_specifier_id ); + result &= ser.write( path + "m_unit_version", m_unit_version ); + result &= ser.write( path + "m_isIsoResourceManager", m_isIsoResourceManager ); + result &= ser.write( path + "m_isCycleMasterCapable", m_isCycleMasterCapable ); + result &= ser.write( path + "m_isSupportIsoOperations", m_isSupportIsoOperations ); + result &= ser.write( path + "m_isBusManagerCapable", m_isBusManagerCapable ); + result &= ser.write( path + "m_cycleClkAcc", m_cycleClkAcc ); + result &= ser.write( path + "m_maxRec", m_maxRec ); + result &= ser.write( path + "m_nodeVendorId", m_nodeVendorId ); + result &= ser.write( path + "m_chipIdHi", m_chipIdHi ); + result &= ser.write( path + "m_chipIdLow", m_chipIdLow ); + return result; +} + +ConfigRom* +ConfigRom::deserialize( std::string path, Util::IODeserialize& deser, Ieee1394Service& ieee1394Service ) +{ + ConfigRom* pConfigRom = new ConfigRom; + if ( !pConfigRom ) { + return 0; + } + + pConfigRom->m_1394Service = ieee1394Service; + + bool result; + result = deser.read( path + "m_nodeId", pConfigRom->m_nodeId ); + result &= deser.read( path + "m_avcDevice", pConfigRom->m_avcDevice ); + result &= deser.read( path + "m_guid", pConfigRom->m_guid ); + result &= deser.read( path + "m_vendorName", pConfigRom->m_vendorName ); + result &= deser.read( path + "m_modelName", pConfigRom->m_modelName ); + result &= deser.read( path + "m_vendorId", pConfigRom->m_vendorId ); + result &= deser.read( path + "m_modelId", pConfigRom->m_modelId ); + result &= deser.read( path + "m_unit_specifier_id", pConfigRom->m_unit_specifier_id ); + result &= deser.read( path + "m_unit_version", pConfigRom->m_unit_version ); + result &= deser.read( path + "m_isIsoResourceManager", pConfigRom->m_isIsoResourceManager ); + result &= deser.read( path + "m_isCycleMasterCapable", pConfigRom->m_isCycleMasterCapable ); + result &= deser.read( path + "m_isSupportIsoOperations", pConfigRom->m_isSupportIsoOperations ); + result &= deser.read( path + "m_isBusManagerCapable", pConfigRom->m_isBusManagerCapable ); + result &= deser.read( path + "m_cycleClkAcc", pConfigRom->m_cycleClkAcc ); + result &= deser.read( path + "m_maxRec", pConfigRom->m_maxRec ); + result &= deser.read( path + "m_nodeVendorId", pConfigRom->m_nodeVendorId ); + result &= deser.read( path + "m_chipIdHi", pConfigRom->m_chipIdHi ); + result &= deser.read( path + "m_chipIdLow", pConfigRom->m_chipIdLow ); + + if ( !result ) { + delete pConfigRom; + return 0; + } + + return pConfigRom; +} + +bool +ConfigRom::setNodeId( fb_nodeid_t nodeId ) +{ + m_nodeId = nodeId; + return true; +} Index: /branches/libffado-2.0/src/libieee1394/IsoHandlerManager.cpp =================================================================== --- /branches/libffado-2.0/src/libieee1394/IsoHandlerManager.cpp (revision 1172) +++ /branches/libffado-2.0/src/libieee1394/IsoHandlerManager.cpp (revision 1172) @@ -0,0 +1,1009 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "config.h" +#include "IsoHandlerManager.h" +#include "ieee1394service.h" +#include "libstreaming/generic/StreamProcessor.h" + +#include "libutil/Atomic.h" +#include "libutil/PosixThread.h" +#include "libutil/SystemTimeSource.h" +#include "libutil/Watchdog.h" + +#include + +IMPL_DEBUG_MODULE( IsoHandlerManager, IsoHandlerManager, DEBUG_LEVEL_NORMAL ); +IMPL_DEBUG_MODULE( IsoTask, IsoTask, DEBUG_LEVEL_NORMAL ); + +using namespace Streaming; + +// --- ISO Thread --- // + +IsoTask::IsoTask(IsoHandlerManager& manager, enum IsoHandler::EHandlerType t) + : m_manager( manager ) + , m_SyncIsoHandler ( NULL ) + , m_handlerType( t ) +{ +} + +IsoTask::~IsoTask() +{ + sem_destroy(&m_activity_semaphore); +} + +bool +IsoTask::Init() +{ + request_update = 0; + + int i; + for (i=0; i < ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT; i++) { + m_IsoHandler_map_shadow[i] = NULL; + m_poll_fds_shadow[i].events = 0; + } + m_poll_nfds_shadow = 0; + + #ifdef DEBUG + m_last_loop_entry = 0; + m_successive_short_loops = 0; + #endif + + sem_init(&m_activity_semaphore, 0, 0); + return true; +} + +bool +IsoTask::requestShadowMapUpdate() +{ + debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) enter\n", this); + INC_ATOMIC(&request_update); + return true; +} + +// updates the internal stream map +// note that this should be executed with the guarantee that +// nobody will modify the parent data structures +void +IsoTask::updateShadowMapHelper() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) updating shadow vars...\n", this); + unsigned int i, cnt, max; + max = m_manager.m_IsoHandlers.size(); + m_SyncIsoHandler = NULL; + for (i = 0, cnt = 0; i < max; i++) { + IsoHandler *h = m_manager.m_IsoHandlers.at(i); + assert(h); + + // skip the handlers not intended for us + if(h->getType() != m_handlerType) continue; + + if (h->isEnabled()) { + m_IsoHandler_map_shadow[cnt] = h; + m_poll_fds_shadow[cnt].fd = h->getFileDescriptor(); + m_poll_fds_shadow[cnt].revents = 0; + m_poll_fds_shadow[cnt].events = POLLIN; + cnt++; + // FIXME: need a more generic approach here + if( m_SyncIsoHandler == NULL + && h->getType() == IsoHandler::eHT_Transmit) { + m_SyncIsoHandler = h; + } + + debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) %s handler %p added\n", + this, h->getTypeString(), h); + } else { + debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) %s handler %p skipped (disabled)\n", + this, h->getTypeString(), h); + } + if(cnt > ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT) { + debugWarning("Too much ISO Handlers in thread...\n"); + break; + } + } + + // FIXME: need a more generic approach here + // if there are no active transmit handlers, + // use the first receive handler + if( m_SyncIsoHandler == NULL + && m_poll_nfds_shadow) { + m_SyncIsoHandler = m_IsoHandler_map_shadow[0]; + } + m_poll_nfds_shadow = cnt; + debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) updated shadow vars...\n", this); +} + +bool +IsoTask::Execute() +{ + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "(%p, %s) Execute\n", + this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive")); + int err; + unsigned int i; + unsigned int m_poll_timeout = 10; + + #ifdef DEBUG + uint64_t now = Util::SystemTimeSource::getCurrentTimeAsUsecs(); + int diff = now - m_last_loop_entry; + if(diff < 100) { + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "(%p, %s) short loop detected (%d usec), cnt: %d\n", + this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive"), + diff, m_successive_short_loops); + m_successive_short_loops++; + if(m_successive_short_loops > 10000) { + debugError("Shutting down runaway thread\n"); + return false; + } + } else { + // reset the counter + m_successive_short_loops = 0; + } + m_last_loop_entry = now; + #endif + + // if some other thread requested a shadow map update, do it + if(request_update) { + updateShadowMapHelper(); + DEC_ATOMIC(&request_update); // ack the update + assert(request_update >= 0); + } + + // bypass if no handlers are registered + if (m_poll_nfds_shadow == 0) { + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "(%p, %s) bypass iterate since no handlers to poll\n", + this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive")); + usleep(m_poll_timeout * 1000); + return true; + } + + // FIXME: what can happen is that poll() returns, but not all clients are + // ready. there might be some busy waiting behavior that still has to be solved. + + // setup the poll here + // we should prevent a poll() where no events are specified, since that will only time-out + bool no_one_to_poll = true; + while(no_one_to_poll) { + for (i = 0; i < m_poll_nfds_shadow; i++) { + short events = 0; + IsoHandler *h = m_IsoHandler_map_shadow[i]; + // we should only poll on a transmit handler + // that has a client that is ready to send + // something. Otherwise it will end up in + // busy wait looping since the packet function + // will defer processing (also avoids the + // AGAIN problem) + if (h->canIterateClient()) { + events = POLLIN | POLLPRI; + no_one_to_poll = false; + // if we are going to poll() it, let's ensure + // it can run until someone wants it to exit + h->allowIterateLoop(); + } + m_poll_fds_shadow[i].events = events; + } + + if(no_one_to_poll) { + debugOutputExtreme(DEBUG_LEVEL_VERBOSE, + "(%p, %s) No one to poll, waiting for something to happen\n", + this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive")); + // wait for something to happen + switch(waitForActivity()) { + case IsoTask::eAR_Error: + debugError("Error while waiting for activity\n"); + return false; + case IsoTask::eAR_Interrupted: + // FIXME: what to do here? + debugWarning("Interrupted while waiting for activity\n"); + break; + case IsoTask::eAR_Timeout: + // FIXME: what to do here? + debugWarning("Timeout while waiting for activity\n"); + break; + case IsoTask::eAR_Activity: + // do nothing + debugOutputExtreme(DEBUG_LEVEL_VERBOSE, + "(%p, %s) something happened\n", + this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive")); + break; + } + } + } + + // Use a shadow map of the fd's such that we don't have to update + // the fd map everytime we run poll(). + err = poll (m_poll_fds_shadow, m_poll_nfds_shadow, m_poll_timeout); + + if (err < 0) { + if (errno == EINTR) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Ignoring poll return due to signal\n"); + return true; + } + debugFatal("poll error: %s\n", strerror (errno)); + return false; + } + + for (i = 0; i < m_poll_nfds_shadow; i++) { + #ifdef DEBUG + if(m_poll_fds_shadow[i].revents) { + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, + "(%p, %s) received events: %08X for (%d/%d, %p, %s)\n", + this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive"), + m_poll_fds_shadow[i].revents, + i, m_poll_nfds_shadow, + m_IsoHandler_map_shadow[i], + m_IsoHandler_map_shadow[i]->getTypeString()); + } + #endif + + // if we get here, it means two things: + // 1) the kernel can accept or provide packets (poll returned POLLIN) + // 2) the client can provide or accept packets (since we enabled polling) + if(m_poll_fds_shadow[i].revents & (POLLIN)) { + m_IsoHandler_map_shadow[i]->iterate(); + } else { + // there might be some error condition + if (m_poll_fds_shadow[i].revents & POLLERR) { + debugWarning("(%p) error on fd for %d\n", this, i); + } + if (m_poll_fds_shadow[i].revents & POLLHUP) { + debugWarning("(%p) hangup on fd for %d\n", this, i); + } + } + +// #ifdef DEBUG +// // check if the handler is still alive +// if(m_IsoHandler_map_shadow[i]->isDead()) { +// debugError("Iso handler (%p, %s) is dead!\n", +// m_IsoHandler_map_shadow[i], +// m_IsoHandler_map_shadow[i]->getTypeString()); +// return false; // shutdown the system +// } +// #endif + + } + return true; +} + +enum IsoTask::eActivityResult +IsoTask::waitForActivity() +{ + debugOutputExtreme(DEBUG_LEVEL_VERBOSE, + "(%p, %s) waiting for activity\n", + this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive")); + struct timespec ts; + int result; + + if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { + debugError("clock_gettime failed\n"); + return eAR_Error; + } + long long int timeout_nsec=0; + int timeout_sec = 0; + + timeout_nsec = ISOHANDLERMANAGER_ISO_TASK_WAIT_TIMEOUT_USECS * 1000LL; + timeout_sec = 0; + while(timeout_nsec >= 1000000000LL) { + timeout_sec += 1; + timeout_nsec -= 1000000000LL; + } + ts.tv_nsec += timeout_nsec; + ts.tv_sec += timeout_sec; + + result = sem_timedwait(&m_activity_semaphore, &ts); + + if(result != 0) { + if (errno == ETIMEDOUT) { + debugOutput(DEBUG_LEVEL_VERBOSE, + "(%p) sem_timedwait() timed out (result=%d)\n", + this, result); + return eAR_Timeout; + } else if (errno == EINTR) { + debugOutput(DEBUG_LEVEL_VERBOSE, + "(%p) sem_timedwait() interrupted by signal (result=%d)\n", + this, result); + return eAR_Interrupted; + } else { + debugError("(%p) sem_timedwait error (result=%d errno=%d)\n", + this, result, errno); + debugError("(%p) timeout_sec=%d timeout_nsec=%lld ts.sec=%d ts.nsec=%lld\n", + this, timeout_sec, timeout_nsec, ts.tv_sec, ts.tv_nsec); + return eAR_Error; + } + } + + debugOutputExtreme(DEBUG_LEVEL_VERBOSE, + "(%p, %s) got activity\n", + this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive")); + return eAR_Activity; +} + +void +IsoTask::signalActivity() +{ + // signal the activity cond var + sem_post(&m_activity_semaphore); + debugOutputExtreme(DEBUG_LEVEL_VERBOSE, + "(%p, %s) activity\n", + this, (m_handlerType == IsoHandler::eHT_Transmit? "Transmit": "Receive")); +} + +void IsoTask::setVerboseLevel(int i) { + setDebugLevel(i); +} + +// -- the ISO handler manager -- // +IsoHandlerManager::IsoHandlerManager(Ieee1394Service& service) + : m_State(E_Created) + , m_service( service ) + , m_realtime(false), m_priority(0) + , m_IsoThreadTransmit ( NULL ) + , m_IsoTaskTransmit ( NULL ) + , m_IsoThreadReceive ( NULL ) + , m_IsoTaskReceive ( NULL ) +{ +} + +IsoHandlerManager::IsoHandlerManager(Ieee1394Service& service, bool run_rt, int rt_prio) + : m_State(E_Created) + , m_service( service ) + , m_realtime(run_rt), m_priority(rt_prio) + , m_IsoThreadTransmit ( NULL ) + , m_IsoTaskTransmit ( NULL ) + , m_IsoThreadReceive ( NULL ) + , m_IsoTaskReceive ( NULL ) +{ +} + +IsoHandlerManager::~IsoHandlerManager() +{ + stopHandlers(); + pruneHandlers(); + if(m_IsoHandlers.size() > 0) { + debugError("Still some handlers in use\n"); + } + if (m_IsoThreadTransmit) { + m_IsoThreadTransmit->Stop(); + delete m_IsoThreadTransmit; + } + if (m_IsoThreadReceive) { + m_IsoThreadReceive->Stop(); + delete m_IsoThreadReceive; + } + if (m_IsoTaskTransmit) { + delete m_IsoTaskTransmit; + } + if (m_IsoTaskReceive) { + delete m_IsoTaskReceive; + } +} + +void +IsoHandlerManager::requestShadowMapUpdate() +{ + if(m_IsoTaskTransmit) m_IsoTaskTransmit->requestShadowMapUpdate(); + if(m_IsoTaskReceive) m_IsoTaskReceive->requestShadowMapUpdate(); +} + +bool +IsoHandlerManager::setThreadParameters(bool rt, int priority) { + debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) switch to: (rt=%d, prio=%d)...\n", this, rt, priority); + if (priority > THREAD_MAX_RTPRIO) priority = THREAD_MAX_RTPRIO; // cap the priority + m_realtime = rt; + m_priority = priority; + + if (m_IsoThreadTransmit) { + if (m_realtime) { + m_IsoThreadTransmit->AcquireRealTime(m_priority + + ISOHANDLERMANAGER_ISO_PRIO_INCREASE + + ISOHANDLERMANAGER_ISO_PRIO_INCREASE_XMIT); + } else { + m_IsoThreadTransmit->DropRealTime(); + } + } + if (m_IsoThreadReceive) { + if (m_realtime) { + m_IsoThreadReceive->AcquireRealTime(m_priority + + ISOHANDLERMANAGER_ISO_PRIO_INCREASE + + ISOHANDLERMANAGER_ISO_PRIO_INCREASE_RECV); + } else { + m_IsoThreadReceive->DropRealTime(); + } + } + + return true; +} + +bool IsoHandlerManager::init() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Initializing ISO manager %p...\n", this); + // check state + if(m_State != E_Created) { + debugError("Manager already initialized...\n"); + return false; + } + + // create threads to iterate our ISO handlers + debugOutput( DEBUG_LEVEL_VERBOSE, "Create iso thread for %p transmit...\n", this); + m_IsoTaskTransmit = new IsoTask( *this, IsoHandler::eHT_Transmit ); + if(!m_IsoTaskTransmit) { + debugFatal("No task\n"); + return false; + } + m_IsoTaskTransmit->setVerboseLevel(getDebugLevel()); + m_IsoThreadTransmit = new Util::PosixThread(m_IsoTaskTransmit, m_realtime, + m_priority + ISOHANDLERMANAGER_ISO_PRIO_INCREASE + + ISOHANDLERMANAGER_ISO_PRIO_INCREASE_XMIT, + PTHREAD_CANCEL_DEFERRED); + + if(!m_IsoThreadTransmit) { + debugFatal("No thread\n"); + return false; + } + m_IsoThreadTransmit->setVerboseLevel(getDebugLevel()); + + debugOutput( DEBUG_LEVEL_VERBOSE, "Create iso thread for %p receive...\n", this); + m_IsoTaskReceive = new IsoTask( *this, IsoHandler::eHT_Receive ); + if(!m_IsoTaskReceive) { + debugFatal("No task\n"); + return false; + } + m_IsoTaskReceive->setVerboseLevel(getDebugLevel()); + m_IsoThreadReceive = new Util::PosixThread(m_IsoTaskReceive, m_realtime, + m_priority + ISOHANDLERMANAGER_ISO_PRIO_INCREASE + + ISOHANDLERMANAGER_ISO_PRIO_INCREASE_RECV, + PTHREAD_CANCEL_DEFERRED); + + if(!m_IsoThreadReceive) { + debugFatal("No thread\n"); + return false; + } + m_IsoThreadReceive->setVerboseLevel(getDebugLevel()); + // register the thread with the RT watchdog + Util::Watchdog *watchdog = m_service.getWatchdog(); + if(watchdog) { + if(!watchdog->registerThread(m_IsoThreadTransmit)) { + debugWarning("could not register iso transmit thread with watchdog\n"); + } + if(!watchdog->registerThread(m_IsoThreadReceive)) { + debugWarning("could not register iso receive thread with watchdog\n"); + } + } else { + debugWarning("could not find valid watchdog\n"); + } + + if (m_IsoThreadTransmit->Start() != 0) { + debugFatal("Could not start ISO Transmit thread\n"); + return false; + } + if (m_IsoThreadReceive->Start() != 0) { + debugFatal("Could not start ISO Receive thread\n"); + return false; + } + + m_State=E_Running; + return true; +} + +bool +IsoHandlerManager::disable(IsoHandler *h) { + bool result; + int i=0; + debugOutput(DEBUG_LEVEL_VERBOSE, "Disable on IsoHandler %p\n", h); + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + if ((*it) == h) { + result = h->disable(); + if(h->getType() == IsoHandler::eHT_Transmit) { + result &= m_IsoTaskTransmit->requestShadowMapUpdate(); + } else { + result &= m_IsoTaskReceive->requestShadowMapUpdate(); + } + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " disabled\n"); + return result; + } + i++; + } + debugError("Handler not found\n"); + return false; +} + +bool +IsoHandlerManager::enable(IsoHandler *h) { + bool result; + int i=0; + debugOutput(DEBUG_LEVEL_VERBOSE, "Enable on IsoHandler %p\n", h); + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + if ((*it) == h) { + result = h->enable(); + if(h->getType() == IsoHandler::eHT_Transmit) { + result &= m_IsoTaskTransmit->requestShadowMapUpdate(); + } else { + result &= m_IsoTaskReceive->requestShadowMapUpdate(); + } + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " enabled\n"); + return result; + } + i++; + } + debugError("Handler not found\n"); + return false; +} + +void +IsoHandlerManager::signalActivityTransmit() +{ + assert(m_IsoTaskTransmit); + m_IsoTaskTransmit->signalActivity(); +} + +void +IsoHandlerManager::signalActivityReceive() +{ + assert(m_IsoTaskReceive); + m_IsoTaskReceive->signalActivity(); +} + +bool IsoHandlerManager::registerHandler(IsoHandler *handler) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n"); + assert(handler); + handler->setVerboseLevel(getDebugLevel()); + m_IsoHandlers.push_back(handler); + requestShadowMapUpdate(); + return true; +} + +bool IsoHandlerManager::unregisterHandler(IsoHandler *handler) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n"); + assert(handler); + + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + if ( *it == handler ) { + m_IsoHandlers.erase(it); + requestShadowMapUpdate(); + return true; + } + } + debugFatal("Could not find handler (%p)\n", handler); + return false; //not found +} + +/** + * Registers an StreamProcessor with the IsoHandlerManager. + * + * If nescessary, an IsoHandler is created to handle this stream. + * Once an StreamProcessor is registered to the handler, it will be included + * in the ISO streaming cycle (i.e. receive/transmit of it will occur). + * + * @param stream the stream to register + * @return true if registration succeeds + * + * \todo : currently there is a one-to-one mapping + * between streams and handlers, this is not ok for + * multichannel receive + */ +bool IsoHandlerManager::registerStream(StreamProcessor *stream) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Registering stream %p\n",stream); + assert(stream); + + IsoHandler* h = NULL; + + // make sure the stream isn't already attached to a handler + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + if((*it)->isStreamRegistered(stream)) { + debugError( "stream already registered!\n"); + return false; + } + } + + // clean up all handlers that aren't used + pruneHandlers(); + + // allocate a handler for this stream + if (stream->getType()==StreamProcessor::ePT_Receive) { + // setup the optimal parameters for the raw1394 ISO buffering + unsigned int packets_per_period = stream->getPacketsPerPeriod(); + unsigned int max_packet_size = stream->getMaxPacketSize(); + unsigned int page_size = getpagesize() - 2; // for one reason or another this is necessary + + // Ensure we don't request a packet size bigger than the + // kernel-enforced maximum which is currently 1 page. + if (max_packet_size > page_size) { + debugError("max packet size (%u) > page size (%u)\n", max_packet_size, page_size); + return false; + } + + unsigned int irq_interval = packets_per_period / MINIMUM_INTERRUPTS_PER_PERIOD; + if(irq_interval <= 0) irq_interval=1; + + // the receive buffer size doesn't matter for the latency, + // but it has a minimal value in order for libraw to operate correctly (300) + int buffers=400; + + // create the actual handler + h = new IsoHandler(*this, IsoHandler::eHT_Receive, + buffers, max_packet_size, irq_interval); + + debugOutput( DEBUG_LEVEL_VERBOSE, " creating IsoRecvHandler\n"); + + if(!h) { + debugFatal("Could not create IsoRecvHandler\n"); + return false; + } + + } else if (stream->getType()==StreamProcessor::ePT_Transmit) { + // setup the optimal parameters for the raw1394 ISO buffering +// unsigned int packets_per_period = stream->getPacketsPerPeriod(); + unsigned int max_packet_size = stream->getMaxPacketSize(); +// unsigned int page_size = getpagesize(); + + // Ensure we don't request a packet size bigger than the + // kernel-enforced maximum which is currently 1 page. +// if (max_packet_size > page_size) { +// debugError("max packet size (%u) > page size (%u)\n", max_packet_size, page_size); +// return false; +// } + if (max_packet_size > MAX_XMIT_PACKET_SIZE) { + debugError("max packet size (%u) > MAX_XMIT_PACKET_SIZE (%u)\n", + max_packet_size, MAX_XMIT_PACKET_SIZE); + return false; + } + + // the SP specifies how many packets to ISO-buffer + int buffers = stream->getNbPacketsIsoXmitBuffer(); + if (buffers > MAX_XMIT_NB_BUFFERS) { + debugOutput(DEBUG_LEVEL_VERBOSE, + "nb buffers (%u) > MAX_XMIT_NB_BUFFERS (%u)\n", + buffers, MAX_XMIT_NB_BUFFERS); + buffers = MAX_XMIT_NB_BUFFERS; + } + unsigned int irq_interval = buffers / MINIMUM_INTERRUPTS_PER_PERIOD; + if(irq_interval <= 0) irq_interval=1; + + debugOutput( DEBUG_LEVEL_VERBOSE, " creating IsoXmitHandler\n"); + + // create the actual handler + h = new IsoHandler(*this, IsoHandler::eHT_Transmit, + buffers, max_packet_size, irq_interval); + + if(!h) { + debugFatal("Could not create IsoXmitHandler\n"); + return false; + } + + } else { + debugFatal("Bad stream type\n"); + return false; + } + + h->setVerboseLevel(getDebugLevel()); + + // init the handler + if(!h->init()) { + debugFatal("Could not initialize receive handler\n"); + return false; + } + + // register the stream with the handler + if(!h->registerStream(stream)) { + debugFatal("Could not register receive stream with handler\n"); + return false; + } + + // register the handler with the manager + if(!registerHandler(h)) { + debugFatal("Could not register receive handler with manager\n"); + return false; + } + debugOutput( DEBUG_LEVEL_VERBOSE, " registered stream (%p) with handler (%p)\n", stream, h); + + m_StreamProcessors.push_back(stream); + debugOutput( DEBUG_LEVEL_VERBOSE, " %d streams, %d handlers registered\n", + m_StreamProcessors.size(), m_IsoHandlers.size()); + return true; +} + +bool IsoHandlerManager::unregisterStream(StreamProcessor *stream) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Unregistering stream %p\n",stream); + assert(stream); + + // make sure the stream isn't attached to a handler anymore + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + if((*it)->isStreamRegistered(stream)) { + if(!(*it)->unregisterStream(stream)) { + debugOutput( DEBUG_LEVEL_VERBOSE, " could not unregister stream (%p) from handler (%p)...\n",stream,*it); + return false; + } + debugOutput( DEBUG_LEVEL_VERBOSE, " unregistered stream (%p) from handler (%p)...\n",stream,*it); + } + } + + // clean up all handlers that aren't used + pruneHandlers(); + + // remove the stream from the registered streams list + for ( StreamProcessorVectorIterator it = m_StreamProcessors.begin(); + it != m_StreamProcessors.end(); + ++it ) + { + if ( *it == stream ) { + m_StreamProcessors.erase(it); + debugOutput( DEBUG_LEVEL_VERBOSE, " deleted stream (%p) from list...\n", *it); + return true; + } + } + return false; //not found +} + +/** + * @brief unregister a handler from the manager + * @note called without the lock held. + */ +void IsoHandlerManager::pruneHandlers() { + debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n"); + IsoHandlerVector toUnregister; + + // find all handlers that are not in use + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + if(!((*it)->inUse())) { + debugOutput( DEBUG_LEVEL_VERBOSE, " handler (%p) not in use\n",*it); + toUnregister.push_back(*it); + } + } + // delete them + for ( IsoHandlerVectorIterator it = toUnregister.begin(); + it != toUnregister.end(); + ++it ) + { + unregisterHandler(*it); + + debugOutput( DEBUG_LEVEL_VERBOSE, " deleting handler (%p)\n",*it); + + // Now the handler's been unregistered it won't be reused + // again. Therefore it really needs to be formally deleted + // to free up the raw1394 handle. Otherwise things fall + // apart after several xrun recoveries as the system runs + // out of resources to support all the disused but still + // allocated raw1394 handles. At least this is the current + // theory as to why we end up with "memory allocation" + // failures after several Xrun recoveries. + delete *it; + } +} + +bool +IsoHandlerManager::stopHandlerForStream(Streaming::StreamProcessor *stream) { + // check state + if(m_State != E_Running) { + debugError("Incorrect state, expected E_Running, got %s\n", eHSToString(m_State)); + return false; + } + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + if((*it)->isStreamRegistered(stream)) { + debugOutput( DEBUG_LEVEL_VERBOSE, " stopping handler %p for stream %p\n", *it, stream); + if(!(*it)->disable()) { + debugOutput( DEBUG_LEVEL_VERBOSE, " could not disable handler (%p)\n",*it); + return false; + } + bool result; + if((*it)->getType() == IsoHandler::eHT_Transmit) { + result = m_IsoTaskTransmit->requestShadowMapUpdate(); + } else { + result = m_IsoTaskReceive->requestShadowMapUpdate(); + } + if(!result) { + debugOutput( DEBUG_LEVEL_VERBOSE, " could not update shadow map for handler (%p)\n",*it); + return false; + } + return true; + } + } + debugError("Stream %p has no attached handler\n", stream); + return false; +} + +int +IsoHandlerManager::getPacketLatencyForStream(Streaming::StreamProcessor *stream) { + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + if((*it)->isStreamRegistered(stream)) { + return (*it)->getPacketLatency(); + } + } + debugError("Stream %p has no attached handler\n", stream); + return 0; +} + +void +IsoHandlerManager::flushHandlerForStream(Streaming::StreamProcessor *stream) { + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + if((*it)->isStreamRegistered(stream)) { + return (*it)->flush(); + } + } + debugError("Stream %p has no attached handler\n", stream); + return; +} + +bool +IsoHandlerManager::startHandlerForStream(Streaming::StreamProcessor *stream) { + return startHandlerForStream(stream, -1); +} + +bool +IsoHandlerManager::startHandlerForStream(Streaming::StreamProcessor *stream, int cycle) { + // check state + if(m_State != E_Running) { + debugError("Incorrect state, expected E_Running, got %s\n", eHSToString(m_State)); + return false; + } + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + if((*it)->isStreamRegistered(stream)) { + debugOutput( DEBUG_LEVEL_VERBOSE, " starting handler %p for stream %p\n", *it, stream); + if(!(*it)->enable(cycle)) { + debugOutput( DEBUG_LEVEL_VERBOSE, " could not enable handler (%p)\n",*it); + return false; + } + bool result; + if((*it)->getType() == IsoHandler::eHT_Transmit) { + result = m_IsoTaskTransmit->requestShadowMapUpdate(); + } else { + result = m_IsoTaskReceive->requestShadowMapUpdate(); + } + if(!result) { + debugOutput( DEBUG_LEVEL_VERBOSE, " could not update shadow map for handler (%p)\n",*it); + return false; + } + return true; + } + } + debugError("Stream %p has no attached handler\n", stream); + return false; +} + +bool IsoHandlerManager::stopHandlers() { + debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n"); + + // check state + if(m_State != E_Running) { + debugError("Incorrect state, expected E_Running, got %s\n", eHSToString(m_State)); + return false; + } + + bool retval=true; + + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + debugOutput( DEBUG_LEVEL_VERBOSE, "Stopping handler (%p)\n",*it); + if(!(*it)->disable()){ + debugOutput( DEBUG_LEVEL_VERBOSE, " could not stop handler (%p)\n",*it); + retval=false; + } + bool result; + if((*it)->getType() == IsoHandler::eHT_Transmit) { + result = m_IsoTaskTransmit->requestShadowMapUpdate(); + } else { + result = m_IsoTaskReceive->requestShadowMapUpdate(); + } + if(!result) { + debugOutput( DEBUG_LEVEL_VERBOSE, " could not update shadow map for handler (%p)\n",*it); + return false; + } + } + + if (retval) { + m_State=E_Prepared; + } else { + m_State=E_Error; + } + return retval; +} + +bool IsoHandlerManager::reset() { + debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n"); + // check state + if(m_State == E_Error) { + debugFatal("Resetting from error condition not yet supported...\n"); + return false; + } + // if not in an error condition, reset means stop the handlers + return stopHandlers(); +} + +void IsoHandlerManager::setVerboseLevel(int i) { + setDebugLevel(i); + // propagate the debug level + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + (*it)->setVerboseLevel(i); + } + if(m_IsoThreadTransmit) m_IsoThreadTransmit->setVerboseLevel(i); + if(m_IsoTaskTransmit) m_IsoTaskTransmit->setVerboseLevel(i); + if(m_IsoThreadReceive) m_IsoThreadReceive->setVerboseLevel(i); + if(m_IsoTaskReceive) m_IsoTaskReceive->setVerboseLevel(i); +} + +void IsoHandlerManager::dumpInfo() { + #ifdef DEBUG + unsigned int i=0; + debugOutputShort( DEBUG_LEVEL_NORMAL, "Dumping IsoHandlerManager Stream handler information...\n"); + debugOutputShort( DEBUG_LEVEL_NORMAL, " State: %d\n",(int)m_State); + + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + debugOutputShort( DEBUG_LEVEL_NORMAL, " IsoHandler %d (%p)\n",i++,*it); + (*it)->dumpInfo(); + } + #endif +} + +const char * +IsoHandlerManager::eHSToString(enum eHandlerStates s) { + switch (s) { + default: return "Invalid"; + case E_Created: return "Created"; + case E_Prepared: return "Prepared"; + case E_Running: return "Running"; + case E_Error: return "Error"; + } +} Index: /branches/libffado-2.0/src/libieee1394/configrom.h =================================================================== --- /branches/libffado-2.0/src/libieee1394/configrom.h (revision 1154) +++ /branches/libffado-2.0/src/libieee1394/configrom.h (revision 1154) @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2005-2008 by Daniel Wagner + * Copyright (C) 2005-2008 by Jonathan Woithe + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef CONFIGROM_H +#define CONFIGROM_H + +#include "fbtypes.h" +#include "csr1212.h" + +#include "libutil/serialize.h" +#include "debugmodule/debugmodule.h" + +#include "libcontrol/Element.h" + +#include + +class Ieee1394Service; + +class ConfigRom + : public Control::Element +{ + public: + ConfigRom( Ieee1394Service& ieee1394service, fb_nodeid_t nodeId ); + virtual ~ConfigRom() {}; + + Ieee1394Service& get1394Service(); + + bool initialize(); + + bool operator == ( const ConfigRom& rhs ); + + const fb_nodeid_t getNodeId() const; + const fb_octlet_t getGuid() const; + const std::string getGuidString() const; + const std::string getModelName() const; + const std::string getVendorName() const; + + const unsigned int getModelId() const; + // FIXME: isn't this the same as getNodeVendorId? + const unsigned int getVendorId() const; + const unsigned int getUnitSpecifierId() const; + const unsigned int getUnitVersion() const; + + bool isIsoResourseManager() const + { return m_isIsoResourceManager; } + bool isCycleMasterCapable() const + { return m_isCycleMasterCapable; } + bool isSupportsIsoOperations() const + { return m_isSupportIsoOperations; } + bool isBusManagerCapable() const + { return m_isBusManagerCapable; } + fb_byte_t getCycleClockAccurancy() const + { return m_cycleClkAcc; } + fb_byte_t getMaxRec() const + { return m_maxRec; } + unsigned short getAsyMaxPayload() const; + + fb_quadlet_t getNodeVendorId() const + { return m_nodeVendorId; } + + bool updatedNodeId(); + bool setNodeId( fb_nodeid_t nodeId ); + + /** + * @brief Compares the GUID of two ConfigRom's + * + * This function compares the GUID of two ConfigRom objects and returns true + * if the GUID of @ref a is larger than the GUID of @ref b . This is intended + * to be used with the STL sort() algorithm. + * + * Note that GUID's are converted to integers for this. + * + * @param a pointer to first ConfigRom + * @param b pointer to second ConfigRom + * + * @returns true if the GUID of @ref a is larger than the GUID of @ref b . + */ + static bool compareGUID( const ConfigRom& a, const ConfigRom& b ); + + bool serialize( std::string path, Util::IOSerialize& ser ); + static ConfigRom* deserialize( std::string path, + Util::IODeserialize& deser, + Ieee1394Service& ieee1394Service ); + + void printConfigRomDebug() const; + void printConfigRom() const; + void setVerboseLevel(int level) { + setDebugLevel(level); + Element::setVerboseLevel(level); + } + + protected: + void processUnitDirectory( struct csr1212_csr* csr, + struct csr1212_keyval* ud_kv, + unsigned int* id ); + + void processRootDirectory( struct csr1212_csr* csr ); + + Ieee1394Service& m_1394Service; + fb_nodeid_t m_nodeId; + bool m_avcDevice; + fb_octlet_t m_guid; + std::string m_vendorName; + std::string m_modelName; + unsigned int m_vendorId; + unsigned int m_modelId; + unsigned int m_unit_specifier_id; + unsigned int m_unit_version; + bool m_isIsoResourceManager; + bool m_isCycleMasterCapable; + bool m_isSupportIsoOperations; + bool m_isBusManagerCapable; + fb_byte_t m_cycleClkAcc; + fb_byte_t m_maxRec; + fb_quadlet_t m_nodeVendorId; + fb_byte_t m_chipIdHi; + fb_quadlet_t m_chipIdLow; + + /* only used during parsing */ + struct csr1212_keyval* m_vendorNameKv; + struct csr1212_keyval* m_modelNameKv; + struct csr1212_csr* m_csr; + +private: + ConfigRom( const ConfigRom& ); // do not allow copy ctor + ConfigRom(); // ctor for deserialition + + DECLARE_DEBUG_MODULE; +}; + +#endif /* CONFIGROM_H */ Index: /branches/libffado-2.0/src/libieee1394/IsoHandlerManager.h =================================================================== --- /branches/libffado-2.0/src/libieee1394/IsoHandlerManager.h (revision 1144) +++ /branches/libffado-2.0/src/libieee1394/IsoHandlerManager.h (revision 1144) @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_ISOHANDLERMANAGER__ +#define __FFADO_ISOHANDLERMANAGER__ + +#include "config.h" +#include "debugmodule/debugmodule.h" + +#include "libutil/Thread.h" + +#include "IsoHandler.h" + +#include +#include +#include +#include + +class Ieee1394Service; +//class IsoHandler; +//enum IsoHandler::EHandlerType; + +namespace Streaming { + class StreamProcessor; + class StreamProcessorManager; + typedef std::vector StreamProcessorVector; + typedef std::vector::iterator StreamProcessorVectorIterator; +} + +typedef std::vector IsoHandlerVector; +typedef std::vector::iterator IsoHandlerVectorIterator; + +class IsoHandlerManager; + +// threads that will handle the packet framing +// one thread per direction, as a compromise for one per +// channel and one for all +class IsoTask : public Util::RunnableInterface +{ + public: + IsoTask(IsoHandlerManager& manager, enum IsoHandler::EHandlerType); + virtual ~IsoTask(); + + public: + bool Init(); + bool Execute(); + + /** + * @brief requests the thread to sync it's stream map with the manager + */ + bool requestShadowMapUpdate(); + enum eActivityResult { + eAR_Activity, + eAR_Timeout, + eAR_Interrupted, + eAR_Error + }; + + /** + * @brief signals that something happened in one of the clients of this task + */ + void signalActivity(); + /** + * @brief wait until something happened in one of the clients of this task + */ + enum eActivityResult waitForActivity(); + + void setVerboseLevel(int i); + protected: + IsoHandlerManager& m_manager; + + // the event request structure + int32_t request_update; + + // static allocation due to RT constraints + // this is the map used by the actual thread + // it is a shadow of the m_StreamProcessors vector + struct pollfd m_poll_fds_shadow[ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT]; + IsoHandler * m_IsoHandler_map_shadow[ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT]; + unsigned int m_poll_nfds_shadow; + IsoHandler * m_SyncIsoHandler; + + // updates the streams map + void updateShadowMapHelper(); + +#ifdef DEBUG + uint64_t m_last_loop_entry; + int m_successive_short_loops; +#endif + + // activity signaling + sem_t m_activity_semaphore; + + enum IsoHandler::EHandlerType m_handlerType; + // debug stuff + DECLARE_DEBUG_MODULE; +}; + +/*! +\brief The ISO Handler management class + + This class manages the use of ISO handlers by ISO streams. + You can register an Streaming::StreamProcessor with an IsoHandlerManager. This + manager will assign an IsoHandler to the stream. If nescessary + the manager allocates a new handler. If there is already a handler + that can handle the Streaming::StreamProcessor (e.g. in case of multichannel receive), + it can be assigned. + +*/ + +class IsoHandlerManager +{ + friend class IsoTask; + + public: + + IsoHandlerManager(Ieee1394Service& service); + IsoHandlerManager(Ieee1394Service& service, bool run_rt, int rt_prio); + virtual ~IsoHandlerManager(); + + bool setThreadParameters(bool rt, int priority); + + void setVerboseLevel(int l); ///< set the verbose level + + void dumpInfo(); ///< print some information about the manager to stdout/stderr + + bool registerStream(Streaming::StreamProcessor *); ///< register an iso stream with the manager + bool unregisterStream(Streaming::StreamProcessor *); ///< unregister an iso stream from the manager + + bool startHandlers(); ///< start the managed ISO handlers + bool startHandlers(int cycle); ///< start the managed ISO handlers + bool stopHandlers(); ///< stop the managed ISO handlers + + bool reset(); ///< reset the ISO manager and all streams + bool init(); + + bool disable(IsoHandler *); ///< disables a handler + bool enable(IsoHandler *); ///< enables a handler + + /** + * @brief signals that something happened in one of the clients + */ + void signalActivityTransmit(); + void signalActivityReceive(); + + ///> disables the handler attached to the stream + bool stopHandlerForStream(Streaming::StreamProcessor *); + ///> starts the handler attached to the specific stream + bool startHandlerForStream(Streaming::StreamProcessor *); + ///> starts the handler attached to the specific stream on a specific cycle + bool startHandlerForStream(Streaming::StreamProcessor *, int cycle); + + /** + * returns the latency of a wake-up for this stream. + * The latency is the time it takes for a packet is delivered to the + * stream after it has been received (was on the wire). + * expressed in cycles + */ + int getPacketLatencyForStream(Streaming::StreamProcessor *); + + void flushHandlerForStream(Streaming::StreamProcessor *stream); + + Ieee1394Service& get1394Service() {return m_service;}; + + void requestShadowMapUpdate(); + + // the state machine + private: + enum eHandlerStates { + E_Created, + E_Prepared, + E_Running, + E_Error + }; + + enum eHandlerStates m_State; + const char *eHSToString(enum eHandlerStates); + + private: + Ieee1394Service& m_service; + // note: there is a disctinction between streams and handlers + // because one handler can serve multiple streams (in case of + // multichannel receive) + + // only streams are allowed to be registered externally. + // we allocate a handler if we need one, otherwise the stream + // is assigned to another handler + + // the collection of handlers + IsoHandlerVector m_IsoHandlers; + + bool registerHandler(IsoHandler *); + bool unregisterHandler(IsoHandler *); + void pruneHandlers(); + + // the collection of streams + Streaming::StreamProcessorVector m_StreamProcessors; + + // handler thread/task + bool m_realtime; + int m_priority; + Util::Thread * m_IsoThreadTransmit; + IsoTask * m_IsoTaskTransmit; + Util::Thread * m_IsoThreadReceive; + IsoTask * m_IsoTaskReceive; + + // debug stuff + DECLARE_DEBUG_MODULE; + +}; + +#endif /* __FFADO_ISOHANDLERMANAGER__ */ + + + Index: /branches/libffado-2.0/src/libieee1394/IsoHandler.cpp =================================================================== --- /branches/libffado-2.0/src/libieee1394/IsoHandler.cpp (revision 1172) +++ /branches/libffado-2.0/src/libieee1394/IsoHandler.cpp (revision 1172) @@ -0,0 +1,735 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "config.h" + +#include "IsoHandler.h" +#include "ieee1394service.h" +#include "IsoHandlerManager.h" + +#include "cycletimer.h" + +#include "libstreaming/generic/StreamProcessor.h" +#include "libutil/PosixThread.h" + +#include +#include "libutil/ByteSwap.h" +#include +#include +#include + +#include +using namespace std; +using namespace Streaming; + +IMPL_DEBUG_MODULE( IsoHandler, IsoHandler, DEBUG_LEVEL_NORMAL ); + +/* the C callbacks */ +enum raw1394_iso_disposition +IsoHandler::iso_transmit_handler(raw1394handle_t handle, + unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped1) { + + IsoHandler *xmitHandler = static_cast(raw1394_get_userdata(handle)); + assert(xmitHandler); + unsigned int skipped = (dropped1 & 0xFFFF0000) >> 16; + unsigned int dropped = dropped1 & 0xFFFF; + return xmitHandler->getPacket(data, length, tag, sy, cycle, dropped, skipped); +} + +enum raw1394_iso_disposition +IsoHandler::iso_receive_handler(raw1394handle_t handle, unsigned char *data, + unsigned int length, unsigned char channel, + unsigned char tag, unsigned char sy, unsigned int cycle, + unsigned int dropped1) { + + IsoHandler *recvHandler = static_cast(raw1394_get_userdata(handle)); + assert(recvHandler); + + unsigned int skipped = (dropped1 & 0xFFFF0000) >> 16; + unsigned int dropped = dropped1 & 0xFFFF; + + return recvHandler->putPacket(data, length, channel, tag, sy, cycle, dropped, skipped); +} + +int IsoHandler::busreset_handler(raw1394handle_t handle, unsigned int generation) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Busreset happened, generation %d...\n", generation); + + IsoHandler *handler = static_cast(raw1394_get_userdata(handle)); + assert(handler); + return handler->handleBusReset(generation); +} + +IsoHandler::IsoHandler(IsoHandlerManager& manager, enum EHandlerType t) + : m_manager( manager ) + , m_type ( t ) + , m_handle( 0 ) + , m_buf_packets( 400 ) + , m_max_packet_size( 1024 ) + , m_irq_interval( -1 ) + , m_last_cycle( -1 ) + , m_last_now( 0xFFFFFFFF ) + , m_Client( 0 ) + , m_speed( RAW1394_ISO_SPEED_400 ) + , m_prebuffers( 0 ) + , m_dont_exit_iterate_loop( true ) + , m_State( E_Created ) +#ifdef DEBUG + , m_packets ( 0 ) + , m_dropped( 0 ) + , m_min_ahead( 7999 ) +#endif +{ +} + +IsoHandler::IsoHandler(IsoHandlerManager& manager, enum EHandlerType t, + unsigned int buf_packets, unsigned int max_packet_size, int irq) + : m_manager( manager ) + , m_type ( t ) + , m_handle( 0 ) + , m_buf_packets( buf_packets ) + , m_max_packet_size( max_packet_size ) + , m_irq_interval( irq ) + , m_last_cycle( -1 ) + , m_last_now( 0xFFFFFFFF ) + , m_Client( 0 ) + , m_speed( RAW1394_ISO_SPEED_400 ) + , m_prebuffers( 0 ) + , m_State( E_Created ) +#ifdef DEBUG + , m_packets ( 0 ) + , m_dropped( 0 ) + , m_min_ahead( 7999 ) +#endif +{ +} + +IsoHandler::IsoHandler(IsoHandlerManager& manager, enum EHandlerType t, unsigned int buf_packets, + unsigned int max_packet_size, int irq, + enum raw1394_iso_speed speed) + : m_manager( manager ) + , m_type ( t ) + , m_handle( 0 ) + , m_buf_packets( buf_packets ) + , m_max_packet_size( max_packet_size ) + , m_irq_interval( irq ) + , m_last_cycle( -1 ) + , m_last_now( 0xFFFFFFFF ) + , m_Client( 0 ) + , m_speed( speed ) + , m_prebuffers( 0 ) + , m_State( E_Created ) +#ifdef DEBUG + , m_packets( 0 ) + , m_dropped( 0 ) +#endif +{ +} + +IsoHandler::~IsoHandler() { +// Don't call until libraw1394's raw1394_new_handle() function has been +// fixed to correctly initialise the iso_packet_infos field. Bug is +// confirmed present in libraw1394 1.2.1. In any case, +// raw1394_destroy_handle() will do any iso system shutdown required. +// raw1394_iso_shutdown(m_handle); + if(m_handle) { + if (m_State == E_Running) { + disable(); + } + raw1394_destroy_handle(m_handle); + } +} + +bool +IsoHandler::canIterateClient() +{ + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "checking...\n"); + if(m_Client) { + bool result; + if (m_type == eHT_Receive) { + result = m_Client->canProducePacket(); + } else { + result = m_Client->canConsumePacket(); + } + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " returns %d\n", result); + return result; + } else { + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, " no client\n"); + } + return false; +} + +bool +IsoHandler::iterate() { + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "(%p, %s) Iterating ISO handler...\n", + this, getTypeString()); + if(m_State == E_Running) { +#if ISOHANDLER_FLUSH_BEFORE_ITERATE + flush(); +#endif + m_last_now = m_manager.get1394Service().getCycleTimer(); + if(raw1394_loop_iterate(m_handle)) { + debugError( "IsoHandler (%p): Failed to iterate handler: %s\n", + this, strerror(errno)); + return false; + } + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, "(%p, %s) done interating ISO handler...\n", + this, getTypeString()); + return true; + } else { + debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) Not iterating a non-running handler...\n", + this, getTypeString()); + return false; + } +} + +bool +IsoHandler::init() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "IsoHandler (%p) enter...\n",this); + // check the state + if(m_State != E_Created) { + debugError("Incorrect state, expected E_Created, got %d\n",(int)m_State); + return false; + } + + // the main handle for the ISO traffic + m_handle = raw1394_new_handle_on_port( m_manager.get1394Service().getPort() ); + if ( !m_handle ) { + if ( !errno ) { + debugError("libraw1394 not compatible\n"); + } else { + debugError("Could not get 1394 handle: %s\n", strerror(errno) ); + debugError("Are ieee1394 and raw1394 drivers loaded?\n"); + } + return false; + } + raw1394_set_userdata(m_handle, static_cast(this)); + + // bus reset handling + if(raw1394_busreset_notify (m_handle, RAW1394_NOTIFY_ON)) { + debugWarning("Could not enable busreset notification.\n"); + debugWarning(" Error message: %s\n",strerror(errno)); + debugWarning("Continuing without bus reset support.\n"); + } else { + // apparently this cannot fail + raw1394_set_bus_reset_handler(m_handle, busreset_handler); + } + + // update the internal state + m_State=E_Initialized; + return true; +} + +bool IsoHandler::disable() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "(%p, %s) enter...\n", + this, (m_type==eHT_Receive?"Receive":"Transmit")); + + // check state + if(m_State == E_Prepared) return true; + if(m_State != E_Running) { + debugError("Incorrect state, expected E_Running, got %d\n",(int)m_State); + return false; + } + + // this is put here to try and avoid the + // Runaway context problem + // don't know if it will help though. + raw1394_iso_xmit_sync(m_handle); + raw1394_iso_stop(m_handle); + m_State = E_Prepared; + return true; +} + +/** + * Bus reset handler + * + * @return ? + */ + +int +IsoHandler::handleBusReset(unsigned int generation) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "bus reset...\n"); + + #define CSR_CYCLE_TIME 0x200 + #define CSR_REGISTER_BASE 0xfffff0000000ULL + // do a simple read on ourself in order to update the internal structures + // this avoids read failures after a bus reset + quadlet_t buf=0; + raw1394_read(m_handle, raw1394_get_local_id(m_handle), + CSR_REGISTER_BASE | CSR_CYCLE_TIME, 4, &buf); + + // notify the client of the fact that we have died + m_Client->handlerDied(); + + if(!disable()) { + debugError("(%p) Could not disable IsoHandler\n", this); + } + + // request the manager to update it's shadow map + m_manager.requestShadowMapUpdate(); + return 0; +} + +void IsoHandler::dumpInfo() +{ + int channel=-1; + if (m_Client) channel=m_Client->getChannel(); + + debugOutputShort( DEBUG_LEVEL_NORMAL, " Handler type................: %s\n", + getTypeString()); + debugOutputShort( DEBUG_LEVEL_NORMAL, " Port, Channel...............: %2d, %2d\n", + m_manager.get1394Service().getPort(), channel); + debugOutputShort( DEBUG_LEVEL_NORMAL, " Buffer, MaxPacketSize, IRQ..: %4d, %4d, %4d\n", + m_buf_packets, m_max_packet_size, m_irq_interval); + if (this->getType() == eHT_Transmit) { + debugOutputShort( DEBUG_LEVEL_NORMAL, " Speed, PreBuffers...........: %2d, %2d\n", + m_speed, m_prebuffers); + #ifdef DEBUG + debugOutputShort( DEBUG_LEVEL_NORMAL, " Min ISOXMT bufferfill : %04d\n", m_min_ahead); + #endif + } + #ifdef DEBUG + debugOutputShort( DEBUG_LEVEL_NORMAL, " Last cycle, dropped.........: %4d, %4u\n", + m_last_cycle, m_dropped); + #endif + +} + +void IsoHandler::setVerboseLevel(int l) +{ + setDebugLevel(l); +} + +bool IsoHandler::registerStream(StreamProcessor *stream) +{ + assert(stream); + debugOutput( DEBUG_LEVEL_VERBOSE, "registering stream (%p)\n", stream); + + if (m_Client) { + debugFatal( "Generic IsoHandlers can have only one client\n"); + return false; + } + m_Client=stream; + return true; +} + +bool IsoHandler::unregisterStream(StreamProcessor *stream) +{ + assert(stream); + debugOutput( DEBUG_LEVEL_VERBOSE, "unregistering stream (%p)\n", stream); + + if(stream != m_Client) { + debugFatal( "no client registered\n"); + return false; + } + m_Client=0; + return true; +} + +void IsoHandler::flush() +{ + if(m_type == eHT_Receive) { + raw1394_iso_recv_flush(m_handle); + } else { + // do nothing + } +} + +// ISO packet interface +enum raw1394_iso_disposition IsoHandler::putPacket( + unsigned char *data, unsigned int length, + unsigned char channel, unsigned char tag, unsigned char sy, + unsigned int cycle, unsigned int dropped, unsigned int skipped) { + + // keep track of dropped cycles + int dropped_cycles = 0; + if (m_last_cycle != (int)cycle && m_last_cycle != -1) { + dropped_cycles = diffCycles(cycle, m_last_cycle) - 1; + #ifdef DEBUG + if (dropped_cycles < 0) { + debugWarning("(%p) dropped < 1 (%d), cycle: %d, last_cycle: %d, dropped: %d, 'skipped'=%u\n", + this, dropped_cycles, cycle, m_last_cycle, dropped, skipped); + } + if (dropped_cycles > 0) { + debugOutput(DEBUG_LEVEL_NORMAL, + "(%p) dropped %d packets on cycle %u, 'dropped'=%u, 'skipped'=%u, cycle=%d, m_last_cycle=%d\n", + this, dropped_cycles, cycle, dropped, skipped, cycle, m_last_cycle); + m_dropped += dropped_cycles; + } + #endif + } + m_last_cycle = cycle; + + uint32_t pkt_ctr = cycle << 12; + + // if we assume that one iterate() loop doesn't take longer than 0.5 seconds, + // the seconds field won't change while the iterate loop runs + // this means that we can preset 'now' before running iterate() + uint32_t now_secs = CYCLE_TIMER_GET_SECS(m_last_now); + // causality results in the fact that 'now' is always after 'cycle' + // except if additional packets are received between setting the + // m_last_now and the starting the iterate() loop. + // this causes the m_last_now to be set at a time before the last packet + // in this loop is received. however, it's not going to be >4000 cycles. + // hence: + // - if the m_last_now > cycle, there is no need to unwrap + // both values are within the same second + // - if m_last_now < cycle it can mean two things: + // * m_last_now has wrapped, but is still later than cycle + // hence diffCycles(m_last_now, cycle) > 0. We should unwrap + // * m_last_now has not wrapped, and cycle is ahead of m_last_now + // this means that the cycle is more recent than the saved + // m_last_now value + // . Hence if we calculate + // the unwrapped difference, and it's larger than 0, this means + // that m_last_now is after the current cycle. . + // it m_last_now is before the current cycle, we should not unwrap + // NOTE: another option is to reread the m_last_now + if( (CYCLE_TIMER_GET_CYCLES(m_last_now) < cycle) + && diffCycles(CYCLE_TIMER_GET_CYCLES(m_last_now), cycle) >= 0) { + debugOutputExtreme(DEBUG_LEVEL_VERBOSE, + "unwrapping %d => %d, %d\n", + CYCLE_TIMER_GET_CYCLES(m_last_now), + cycle); + // the cycle field has wrapped, substract one second + if(now_secs == 0) { + now_secs = 127; + } else { + now_secs -= 1; + } + } + + #ifdef DEBUG + if( (CYCLE_TIMER_GET_CYCLES(m_last_now) < cycle) + && diffCycles(CYCLE_TIMER_GET_CYCLES(m_last_now), cycle) < 0 + // ignore this on dropped cycles, since it's normal + // that now is ahead on the received packets (as we miss packets) + && dropped_cycles == 0) + { + debugOutput(DEBUG_LEVEL_VERBOSE, "Special non-unwrapping happened\n"); + } + #endif + pkt_ctr |= (now_secs & 0x7F) << 25; + + #if ISOHANDLER_CHECK_CTR_RECONSTRUCTION + // add a seconds field + uint32_t now = m_manager.get1394Service().getCycleTimer(); + uint32_t now_secs_ref = CYCLE_TIMER_GET_SECS(now); + // causality results in the fact that 'now' is always after 'cycle' + // or at best, equal (if this handler was called within 125us after + // the packet was on the wire). + if(CYCLE_TIMER_GET_CYCLES(now) < cycle) { + // the cycle field has wrapped, substract one second + if(now_secs_ref == 0) { + now_secs_ref = 127; + } else { + now_secs_ref -= 1; + } + } + uint32_t pkt_ctr_ref = cycle << 12; + pkt_ctr_ref |= (now_secs_ref & 0x7F) << 25; + + if(pkt_ctr != pkt_ctr_ref) { + debugWarning("reconstructed CTR counter discrepancy\n"); + debugWarning(" ingredients: %X, %lX, %lX, %lX, %lX, %ld, %ld\n", + cycle, pkt_ctr_ref, pkt_ctr, now, m_last_now, now_secs_ref, now_secs); + } + #endif + + // leave the offset field (for now?) + + debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, + "received packet: length=%d, channel=%d, cycle=%d\n", + length, channel, cycle); + #ifdef DEBUG + m_packets++; + if (length > m_max_packet_size) { + debugWarning("(%p, %s) packet too large: len=%u max=%u\n", + this, getTypeString(), length, m_max_packet_size); + } + if(m_last_cycle == -1) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Handler for %s SP %p is alive (cycle = %u)\n", getTypeString(), this, cycle); + } + #endif + + // iterate the client if required + if(m_Client) { + enum raw1394_iso_disposition retval = m_Client->putPacket(data, length, channel, tag, sy, pkt_ctr, dropped_cycles, skipped); + if (retval == RAW1394_ISO_OK) { + if (m_dont_exit_iterate_loop) { + return RAW1394_ISO_OK; + } else { + m_dont_exit_iterate_loop = true; + debugOutput(DEBUG_LEVEL_VERBOSE, + "(%p) loop exit requested\n", + this); + return RAW1394_ISO_DEFER; + } + } else { + return retval; + } + } + + return RAW1394_ISO_OK; +} + +enum raw1394_iso_disposition +IsoHandler::getPacket(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int skipped) { + + uint32_t pkt_ctr; + if (cycle < 0) { + // mark invalid + pkt_ctr = 0xFFFFFFFF; + } else { + pkt_ctr = cycle << 12; + +#if 0 // we don't need this for xmit + // if we assume that one iterate() loop doesn't take longer than 0.5 seconds, + // the seconds field won't change while the iterate loop runs + // this means that we can preset 'now' before running iterate() + uint32_t now_secs = CYCLE_TIMER_GET_SECS(m_last_now); + // causality results in the fact that 'now' is always after 'cycle' + if(CYCLE_TIMER_GET_CYCLES(m_last_now) > (unsigned int)cycle) { + // the cycle field has wrapped, add one second + now_secs += 1; + // no need for this: + //if(now_secs == 128) { + // now_secs = 0; + //} + // since we mask later on + } + pkt_ctr |= (now_secs & 0x7F) << 25; + + #if ISOHANDLER_CHECK_CTR_RECONSTRUCTION + // add a seconds field + uint32_t now = m_manager.get1394Service().getCycleTimer(); + uint32_t now_secs_ref = CYCLE_TIMER_GET_SECS(now); + // causality results in the fact that 'now' is always after 'cycle' + if(CYCLE_TIMER_GET_CYCLES(now) > (unsigned int)cycle) { + // the cycle field has wrapped, add one second + now_secs_ref += 1; + // no need for this: + //if(now_secs == 128) { + // now_secs = 0; + //} + // since we mask later on + } + uint32_t pkt_ctr_ref = cycle << 12; + pkt_ctr_ref |= (now_secs_ref & 0x7F) << 25; + + if(pkt_ctr != pkt_ctr_ref) { + debugWarning("reconstructed CTR counter discrepancy\n"); + pkt_ctr=pkt_ctr_ref; + } + #endif +#endif + } + + debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, + "sending packet: length=%d, cycle=%d\n", + *length, cycle); + + #ifdef DEBUG + m_packets++; + if(m_last_cycle == -1) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Handler for %s SP %p is alive (cycle = %d)\n", getTypeString(), this, cycle); + } + #endif + + // keep track of dropped cycles + int dropped_cycles = 0; + if (m_last_cycle != cycle && m_last_cycle != -1) { + dropped_cycles = diffCycles(cycle, m_last_cycle) - 1; + // correct for skipped packets + // since those are not dropped, but only delayed + dropped_cycles -= skipped; + + #ifdef DEBUG + if(skipped) { + debugOutput(DEBUG_LEVEL_NORMAL, + "(%p) skipped %d cycles, cycle: %d, last_cycle: %d, dropped: %d\n", + this, skipped, cycle, m_last_cycle, dropped); + } + if (dropped_cycles < 0) { + debugWarning("(%p) dropped < 1 (%d), cycle: %d, last_cycle: %d, dropped: %d, skipped: %d\n", + this, dropped_cycles, cycle, m_last_cycle, dropped, skipped); + } + if (dropped_cycles > 0) { + debugOutput(DEBUG_LEVEL_NORMAL, + "(%p) dropped %d packets on cycle %u (last_cycle=%u, dropped=%d, skipped: %d)\n", + this, dropped_cycles, cycle, m_last_cycle, dropped, skipped); + m_dropped += dropped_cycles - skipped; + } + #endif + } + if (cycle >= 0) { + m_last_cycle = cycle; + + #ifdef DEBUG +/* int ahead = diffCycles(cycle, now_cycles); + if (ahead < m_min_ahead) m_min_ahead = ahead; +*/ + #endif + } + + if(m_Client) { + enum raw1394_iso_disposition retval; + retval = m_Client->getPacket(data, length, tag, sy, pkt_ctr, dropped, skipped, m_max_packet_size); + #ifdef DEBUG + if (*length > m_max_packet_size) { + debugWarning("(%p, %s) packet too large: len=%u max=%u\n", + this, getTypeString(), *length, m_max_packet_size); + } + #endif + if (retval == RAW1394_ISO_OK) { + if (m_dont_exit_iterate_loop) { + return RAW1394_ISO_OK; + } else { + m_dont_exit_iterate_loop = true; + debugOutput(DEBUG_LEVEL_VERBOSE, + "(%p) loop exit requested\n", + this); + return RAW1394_ISO_DEFER; + } + } else { + return retval; + } + } + + *tag = 0; + *sy = 0; + *length = 0; + return RAW1394_ISO_OK; +} + +bool IsoHandler::prepare() +{ + // check the state + if(m_State != E_Initialized) { + debugError("Incorrect state, expected E_Initialized, got %d\n",(int)m_State); + return false; + } + + // Don't call until libraw1394's raw1394_new_handle() function has been + // fixed to correctly initialise the iso_packet_infos field. Bug is + // confirmed present in libraw1394 1.2.1. + // raw1394_iso_shutdown(m_handle); + m_State = E_Prepared; + + debugOutput( DEBUG_LEVEL_VERBOSE, "Preparing iso handler (%p, client=%p)\n", this, m_Client); + dumpInfo(); + if (getType() == eHT_Receive) { + if(m_irq_interval > 1) { + if(raw1394_iso_recv_init(m_handle, + iso_receive_handler, + m_buf_packets, + m_max_packet_size, + m_Client->getChannel(), + RAW1394_DMA_BUFFERFILL, +// RAW1394_DMA_PACKET_PER_BUFFER, + m_irq_interval)) { + debugFatal("Could not do receive initialisation (DMA_BUFFERFILL)!\n" ); + debugFatal(" %s\n",strerror(errno)); + return false; + } + } else { + if(raw1394_iso_recv_init(m_handle, + iso_receive_handler, + m_buf_packets, + m_max_packet_size, + m_Client->getChannel(), + RAW1394_DMA_PACKET_PER_BUFFER, + m_irq_interval)) { + debugFatal("Could not do receive initialisation (PACKET_PER_BUFFER)!\n" ); + debugFatal(" %s\n",strerror(errno)); + return false; + } + } + return true; + } else { + if(raw1394_iso_xmit_init(m_handle, + iso_transmit_handler, + m_buf_packets, + m_max_packet_size, + m_Client->getChannel(), + m_speed, + m_irq_interval)) { + debugFatal("Could not do xmit initialisation!\n" ); + return false; + } + return true; + } +} + +bool IsoHandler::enable(int cycle) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "start on cycle %d\n", cycle); + // check the state + if(m_State != E_Prepared) { + if(!prepare()) { + debugFatal("Could not prepare handler\n"); + return false; + } + } + + if (getType() == eHT_Receive) { + if(raw1394_iso_recv_start(m_handle, cycle, -1, 0)) { + debugFatal("Could not start receive handler (%s)\n",strerror(errno)); + dumpInfo(); + return false; + } + } else { + if(raw1394_iso_xmit_start(m_handle, cycle, m_prebuffers)) { + debugFatal("Could not start xmit handler (%s)\n",strerror(errno)); + dumpInfo(); + return false; + } + } + +#ifdef DEBUG + m_min_ahead = 7999; +#endif + m_State = E_Running; + return true; +} + +/** + * @brief convert a EHandlerType to a string + * @param t the type + * @return a char * describing the state + */ +const char * +IsoHandler::eHTToString(enum EHandlerType t) { + switch (t) { + case eHT_Receive: return "Receive"; + case eHT_Transmit: return "Transmit"; + default: return "error: unknown type"; + } +} Index: /branches/libffado-2.0/src/libieee1394/csr1212.c =================================================================== --- /branches/libffado-2.0/src/libieee1394/csr1212.c (revision 864) +++ /branches/libffado-2.0/src/libieee1394/csr1212.c (revision 864) @@ -0,0 +1,1649 @@ +/* + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ +/* + * csr1212.c -- IEEE 1212 Control and Status Register support for Linux + * + * Copyright (C) 2003 Francois Retief + * Steve Kinneberg + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/* TODO List: + * - Verify interface consistency: i.e., public functions that take a size + * parameter expect size to be in bytes. + * - Convenience functions for reading a block of data from a given offset. + */ + +#ifndef __KERNEL__ +#include +#endif + +#include "csr1212.h" + + +/* Permitted key type for each key id */ +#define __I (1 << CSR1212_KV_TYPE_IMMEDIATE) +#define __C (1 << CSR1212_KV_TYPE_CSR_OFFSET) +#define __D (1 << CSR1212_KV_TYPE_DIRECTORY) +#define __L (1 << CSR1212_KV_TYPE_LEAF) +static const u_int8_t csr1212_key_id_type_map[0x30] = { + 0, /* Reserved */ + __D | __L, /* Descriptor */ + __I | __D | __L, /* Bus_Dependent_Info */ + __I | __D | __L, /* Vendor */ + __I, /* Hardware_Version */ + 0, 0, /* Reserved */ + __D | __L, /* Module */ + 0, 0, 0, 0, /* Reserved */ + __I, /* Node_Capabilities */ + __L, /* EUI_64 */ + 0, 0, 0, /* Reserved */ + __D, /* Unit */ + __I, /* Specifier_ID */ + __I, /* Version */ + __I | __C | __D | __L, /* Dependent_Info */ + __L, /* Unit_Location */ + 0, /* Reserved */ + __I, /* Model */ + __D, /* Instance */ + __L, /* Keyword */ + __D, /* Feature */ + __L, /* Extended_ROM */ + __I, /* Extended_Key_Specifier_ID */ + __I, /* Extended_Key */ + __I | __C | __D | __L, /* Extended_Data */ + __L, /* Modifiable_Descriptor */ + __I, /* Directory_ID */ + __I, /* Revision */ +}; +#undef __I +#undef __C +#undef __D +#undef __L + + +#define quads_to_bytes(_q) ((_q) * sizeof(u_int32_t)) +#define bytes_to_quads(_b) (((_b) + sizeof(u_int32_t) - 1) / sizeof(u_int32_t)) + +static inline void free_keyval(struct csr1212_keyval *kv) +{ + if ((kv->key.type == CSR1212_KV_TYPE_LEAF) && + (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)) + CSR1212_FREE(kv->value.leaf.data); + + CSR1212_FREE(kv); +} + +static u_int16_t csr1212_crc16(const u_int32_t *buffer, size_t length) +{ + int shift; + u_int32_t data; + u_int16_t sum, crc = 0; + + for (; length; length--) { + data = CSR1212_BE32_TO_CPU(*buffer); + buffer++; + for (shift = 28; shift >= 0; shift -= 4 ) { + sum = ((crc >> 12) ^ (data >> shift)) & 0xf; + crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); + } + crc &= 0xffff; + } + + return CSR1212_CPU_TO_BE16(crc); +} + +#if 0 +/* Microsoft computes the CRC with the bytes in reverse order. Therefore we + * have a special version of the CRC algorithm to account for their buggy + * software. */ +static u_int16_t csr1212_msft_crc16(const u_int32_t *buffer, size_t length) +{ + int shift; + u_int32_t data; + u_int16_t sum, crc = 0; + + for (; length; length--) { + data = CSR1212_LE32_TO_CPU(*buffer); + buffer++; + for (shift = 28; shift >= 0; shift -= 4 ) { + sum = ((crc >> 12) ^ (data >> shift)) & 0xf; + crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); + } + crc &= 0xffff; + } + + return CSR1212_CPU_TO_BE16(crc); +} +#endif + +static inline struct csr1212_dentry *csr1212_find_keyval(struct csr1212_keyval *dir, + struct csr1212_keyval *kv) +{ + struct csr1212_dentry *pos; + + for (pos = dir->value.directory.dentries_head; + pos != NULL; pos = pos->next) { + if (pos->kv == kv) + return pos; + } + return NULL; +} + + +static inline struct csr1212_keyval *csr1212_find_keyval_offset(struct csr1212_keyval *kv_list, + u_int32_t offset) +{ + struct csr1212_keyval *kv; + + for (kv = kv_list->next; kv && (kv != kv_list); kv = kv->next) { + if (kv->offset == offset) + return kv; + } + return NULL; +} + + +/* Creation Routines */ +struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops, + size_t bus_info_size, void *private_data) +{ + struct csr1212_csr *csr; + + csr = CSR1212_MALLOC(sizeof(*csr)); + if (!csr) + return NULL; + + csr->cache_head = + csr1212_rom_cache_malloc(CSR1212_CONFIG_ROM_SPACE_OFFSET, + CSR1212_CONFIG_ROM_SPACE_SIZE); + if (!csr->cache_head) { + CSR1212_FREE(csr); + return NULL; + } + + /* The keyval key id is not used for the root node, but a valid key id + * that can be used for a directory needs to be passed to + * csr1212_new_directory(). */ + csr->root_kv = csr1212_new_directory(CSR1212_KV_ID_VENDOR); + if (!csr->root_kv) { + CSR1212_FREE(csr->cache_head); + CSR1212_FREE(csr); + return NULL; + } + + csr->bus_info_data = csr->cache_head->data; + csr->bus_info_len = bus_info_size; + csr->crc_len = bus_info_size; + csr->ops = ops; + csr->private_data = private_data; + csr->cache_tail = csr->cache_head; + + return csr; +} + + + +void csr1212_init_local_csr(struct csr1212_csr *csr, + const u_int32_t *bus_info_data, int max_rom) +{ + static const int mr_map[] = { 4, 64, 1024, 0 }; + +#ifdef __KERNEL__ + BUG_ON(max_rom & ~0x3); + csr->max_rom = mr_map[max_rom]; +#else + if (max_rom & ~0x3) /* caller supplied invalid argument */ + csr->max_rom = 0; + else + csr->max_rom = mr_map[max_rom]; +#endif + memcpy(csr->bus_info_data, bus_info_data, csr->bus_info_len); +} + + +static struct csr1212_keyval *csr1212_new_keyval(u_int8_t type, u_int8_t key) +{ + struct csr1212_keyval *kv; + + if (key < 0x30 && ((csr1212_key_id_type_map[key] & (1 << type)) == 0)) + return NULL; + + kv = CSR1212_MALLOC(sizeof(*kv)); + if (!kv) + return NULL; + + kv->key.type = type; + kv->key.id = key; + + kv->associate = NULL; + kv->refcnt = 1; + + kv->next = NULL; + kv->prev = NULL; + kv->offset = 0; + kv->valid = 0; + return kv; +} + +struct csr1212_keyval *csr1212_new_immediate(u_int8_t key, u_int32_t value) +{ + struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_IMMEDIATE, key); + + if (!kv) + return NULL; + + kv->value.immediate = value; + kv->valid = 1; + return kv; +} + +struct csr1212_keyval *csr1212_new_leaf(u_int8_t key, const void *data, size_t data_len) +{ + struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, key); + + if (!kv) + return NULL; + + if (data_len > 0) { + kv->value.leaf.data = CSR1212_MALLOC(data_len); + if (!kv->value.leaf.data) { + CSR1212_FREE(kv); + return NULL; + } + + if (data) + memcpy(kv->value.leaf.data, data, data_len); + } else { + kv->value.leaf.data = NULL; + } + + kv->value.leaf.len = bytes_to_quads(data_len); + kv->offset = 0; + kv->valid = 1; + + return kv; +} + +struct csr1212_keyval *csr1212_new_csr_offset(u_int8_t key, u_int32_t csr_offset) +{ + struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_CSR_OFFSET, key); + + if (!kv) + return NULL; + + kv->value.csr_offset = csr_offset; + + kv->offset = 0; + kv->valid = 1; + return kv; +} + +struct csr1212_keyval *csr1212_new_directory(u_int8_t key) +{ + struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_DIRECTORY, key); + + if (!kv) + return NULL; + + kv->value.directory.len = 0; + kv->offset = 0; + kv->value.directory.dentries_head = NULL; + kv->value.directory.dentries_tail = NULL; + kv->valid = 1; + return kv; +} + +int csr1212_associate_keyval(struct csr1212_keyval *kv, + struct csr1212_keyval *associate) +{ + if (!kv || !associate) + return CSR1212_EINVAL; + + if (kv->key.id == CSR1212_KV_ID_DESCRIPTOR || + (associate->key.id != CSR1212_KV_ID_DESCRIPTOR && + associate->key.id != CSR1212_KV_ID_DEPENDENT_INFO && + associate->key.id != CSR1212_KV_ID_EXTENDED_KEY && + associate->key.id != CSR1212_KV_ID_EXTENDED_DATA && + associate->key.id < 0x30)) + return CSR1212_EINVAL; + + if (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID && + associate->key.id != CSR1212_KV_ID_EXTENDED_KEY) + return CSR1212_EINVAL; + + if (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY && + associate->key.id != CSR1212_KV_ID_EXTENDED_DATA) + return CSR1212_EINVAL; + + if (associate->key.id == CSR1212_KV_ID_EXTENDED_KEY && + kv->key.id != CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) + return CSR1212_EINVAL; + + if (associate->key.id == CSR1212_KV_ID_EXTENDED_DATA && + kv->key.id != CSR1212_KV_ID_EXTENDED_KEY) + return CSR1212_EINVAL; + + if (kv->associate) + csr1212_release_keyval(kv->associate); + + associate->refcnt++; + kv->associate = associate; + + return CSR1212_SUCCESS; +} + +int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, + struct csr1212_keyval *kv) +{ + struct csr1212_dentry *dentry; + + if (!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY) + return CSR1212_EINVAL; + + dentry = CSR1212_MALLOC(sizeof(*dentry)); + if (!dentry) + return CSR1212_ENOMEM; + + dentry->kv = kv; + + kv->refcnt++; + + dentry->next = NULL; + dentry->prev = dir->value.directory.dentries_tail; + + if (!dir->value.directory.dentries_head) + dir->value.directory.dentries_head = dentry; + + if (dir->value.directory.dentries_tail) + dir->value.directory.dentries_tail->next = dentry; + dir->value.directory.dentries_tail = dentry; + + return CSR1212_SUCCESS; +} + +struct csr1212_keyval *csr1212_new_extended_immediate(u_int32_t spec, u_int32_t key, + u_int32_t value) +{ + struct csr1212_keyval *kvs, *kvk, *kvv; + + kvs = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID, spec); + kvk = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY, key); + kvv = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_DATA, value); + + if (!kvs || !kvk || !kvv) { + if (kvs) + free_keyval(kvs); + if (kvk) + free_keyval(kvk); + if (kvv) + free_keyval(kvv); + return NULL; + } + + /* Don't keep a local reference to the extended key or value. */ + kvk->refcnt = 0; + kvv->refcnt = 0; + + csr1212_associate_keyval(kvk, kvv); + csr1212_associate_keyval(kvs, kvk); + + return kvs; +} + +struct csr1212_keyval *csr1212_new_extended_leaf(u_int32_t spec, u_int32_t key, + const void *data, size_t data_len) +{ + struct csr1212_keyval *kvs, *kvk, *kvv; + + kvs = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID, spec); + kvk = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY, key); + kvv = csr1212_new_leaf(CSR1212_KV_ID_EXTENDED_DATA, data, data_len); + + if (!kvs || !kvk || !kvv) { + if (kvs) + free_keyval(kvs); + if (kvk) + free_keyval(kvk); + if (kvv) + free_keyval(kvv); + return NULL; + } + + /* Don't keep a local reference to the extended key or value. */ + kvk->refcnt = 0; + kvv->refcnt = 0; + + csr1212_associate_keyval(kvk, kvv); + csr1212_associate_keyval(kvs, kvk); + + return kvs; +} + +struct csr1212_keyval *csr1212_new_descriptor_leaf(u_int8_t dtype, u_int32_t specifier_id, + const void *data, size_t data_len) +{ + struct csr1212_keyval *kv; + + kv = csr1212_new_leaf(CSR1212_KV_ID_DESCRIPTOR, NULL, + data_len + CSR1212_DESCRIPTOR_LEAF_OVERHEAD); + if (!kv) + return NULL; + + CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, dtype); + CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, specifier_id); + + if (data) { + memcpy(CSR1212_DESCRIPTOR_LEAF_DATA(kv), data, data_len); + } + + return kv; +} + + +struct csr1212_keyval *csr1212_new_textual_descriptor_leaf(u_int8_t cwidth, + u_int16_t cset, + u_int16_t language, + const void *data, + size_t data_len) +{ + struct csr1212_keyval *kv; + char *lstr; + + kv = csr1212_new_descriptor_leaf(0, 0, NULL, data_len + + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD); + if (!kv) + return NULL; + + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, cwidth); + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, cset); + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language); + + lstr = (char*)CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv); + + /* make sure last quadlet is zeroed out */ + *((u_int32_t*)&(lstr[(data_len - 1) & ~0x3])) = 0; + + /* don't copy the NUL terminator */ + memcpy(lstr, data, data_len); + + return kv; +} + +static int csr1212_check_minimal_ascii(const char *s) +{ + static const char minimal_ascii_table[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x0a, 0x00, 0x0C, 0x0D, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x21, 0x22, 0x00, 0x00, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x5f, + 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + for (; *s; s++) { + if (minimal_ascii_table[*s & 0x7F] != *s) + return -1; /* failed */ + } + /* String conforms to minimal-ascii, as specified by IEEE 1212, + * par. 7.4 */ + return 0; +} + +struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s) +{ + /* Check if string conform to minimal_ascii format */ + if (csr1212_check_minimal_ascii(s)) + return NULL; + + /* IEEE 1212, par. 7.5.4.1 Textual descriptors (minimal ASCII) */ + return csr1212_new_textual_descriptor_leaf(0, 0, 0, s, strlen(s)); +} + +struct csr1212_keyval *csr1212_new_icon_descriptor_leaf(u_int32_t version, + u_int8_t palette_depth, + u_int8_t color_space, + u_int16_t language, + u_int16_t hscan, + u_int16_t vscan, + u_int32_t *palette, + u_int32_t *pixels) +{ + static const int pd[4] = { 0, 4, 16, 256 }; + static const int cs[16] = { 4, 2 }; + struct csr1212_keyval *kv; + int palette_size; + int pixel_size = (hscan * vscan + 3) & ~0x3; + + if (!pixels || (!palette && palette_depth) || + (palette_depth & ~0x3) || (color_space & ~0xf)) + return NULL; + + palette_size = pd[palette_depth] * cs[color_space]; + + kv = csr1212_new_descriptor_leaf(1, 0, NULL, + palette_size + pixel_size + + CSR1212_ICON_DESCRIPTOR_LEAF_OVERHEAD); + if (!kv) + return NULL; + + CSR1212_ICON_DESCRIPTOR_LEAF_SET_VERSION(kv, version); + CSR1212_ICON_DESCRIPTOR_LEAF_SET_PALETTE_DEPTH(kv, palette_depth); + CSR1212_ICON_DESCRIPTOR_LEAF_SET_COLOR_SPACE(kv, color_space); + CSR1212_ICON_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language); + CSR1212_ICON_DESCRIPTOR_LEAF_SET_HSCAN(kv, hscan); + CSR1212_ICON_DESCRIPTOR_LEAF_SET_VSCAN(kv, vscan); + + if (palette_size) + memcpy(CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE(kv), palette, + palette_size); + + memcpy(CSR1212_ICON_DESCRIPTOR_LEAF_PIXELS(kv), pixels, pixel_size); + + return kv; +} + +struct csr1212_keyval *csr1212_new_modifiable_descriptor_leaf(u_int16_t max_size, + u_int64_t address) +{ + struct csr1212_keyval *kv; + + /* IEEE 1212, par. 7.5.4.3 Modifiable descriptors */ + kv = csr1212_new_leaf(CSR1212_KV_ID_MODIFIABLE_DESCRIPTOR, NULL, sizeof(u_int64_t)); + if(!kv) + return NULL; + + CSR1212_MODIFIABLE_DESCRIPTOR_SET_MAX_SIZE(kv, max_size); + CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_HI(kv, address); + CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_LO(kv, address); + + return kv; +} + +static int csr1212_check_keyword(const char *s) +{ + for (; *s; s++) { + + if (('A' <= *s) && (*s <= 'Z')) + continue; + if (('0' <= *s) && (*s <= '9')) + continue; + if (*s == '-') + continue; + + return -1; /* failed */ + } + /* String conforms to keyword, as specified by IEEE 1212, + * par. 7.6.5 */ + return CSR1212_SUCCESS; +} + +struct csr1212_keyval *csr1212_new_keyword_leaf(int strc, const char *strv[]) +{ + struct csr1212_keyval *kv; + char *buffer; + int i, data_len = 0; + + /* Check all keywords to see if they conform to restrictions: + * Only the following characters is allowed ['A'..'Z','0'..'9','-'] + * Each word is zero-terminated. + * Also calculate the total length of the keywords. + */ + for (i = 0; i < strc; i++) { + if (!strv[i] || csr1212_check_keyword(strv[i])) { + return NULL; + } + data_len += strlen(strv[i]) + 1; /* Add zero-termination char. */ + } + + /* IEEE 1212, par. 7.6.5 Keyword leaves */ + kv = csr1212_new_leaf(CSR1212_KV_ID_KEYWORD, NULL, data_len); + if (!kv) + return NULL; + + buffer = (char *)kv->value.leaf.data; + + /* make sure last quadlet is zeroed out */ + *((u_int32_t*)&(buffer[(data_len - 1) & ~0x3])) = 0; + + /* Copy keyword(s) into leaf data buffer */ + for (i = 0; i < strc; i++) { + int len = strlen(strv[i]) + 1; + memcpy(buffer, strv[i], len); + buffer += len; + } + return kv; +} + + +/* Destruction Routines */ + +void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir, + struct csr1212_keyval *kv) +{ + struct csr1212_dentry *dentry; + + if (!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY) + return; + + dentry = csr1212_find_keyval(dir, kv); + + if (!dentry) + return; + + if (dentry->prev) + dentry->prev->next = dentry->next; + if (dentry->next) + dentry->next->prev = dentry->prev; + if (dir->value.directory.dentries_head == dentry) + dir->value.directory.dentries_head = dentry->next; + if (dir->value.directory.dentries_tail == dentry) + dir->value.directory.dentries_tail = dentry->prev; + + CSR1212_FREE(dentry); + + csr1212_release_keyval(kv); +} + + +void csr1212_disassociate_keyval(struct csr1212_keyval *kv) +{ + if (kv->associate) { + csr1212_release_keyval(kv->associate); + } + + kv->associate = NULL; +} + + +/* This function is used to free the memory taken by a keyval. If the given + * keyval is a directory type, then any keyvals contained in that directory + * will be destroyed as well if their respective refcnts are 0. By means of + * list manipulation, this routine will descend a directory structure in a + * non-recursive manner. */ +void _csr1212_destroy_keyval(struct csr1212_keyval *kv) +{ + struct csr1212_keyval *k, *a; + struct csr1212_dentry dentry; + struct csr1212_dentry *head, *tail; + + dentry.kv = kv; + dentry.next = NULL; + dentry.prev = NULL; + + head = &dentry; + tail = head; + + while (head) { + k = head->kv; + + while (k) { + k->refcnt--; + + if (k->refcnt > 0) + break; + + a = k->associate; + + if (k->key.type == CSR1212_KV_TYPE_DIRECTORY) { + /* If the current entry is a directory, then move all + * the entries to the destruction list. */ + if (k->value.directory.dentries_head) { + tail->next = k->value.directory.dentries_head; + k->value.directory.dentries_head->prev = tail; + tail = k->value.directory.dentries_tail; + } + } + free_keyval(k); + k = a; + } + + head = head->next; + if (head) { + if (head->prev && head->prev != &dentry) { + CSR1212_FREE(head->prev); + } + head->prev = NULL; + } else if (tail != &dentry) + CSR1212_FREE(tail); + } +} + + +void csr1212_destroy_csr(struct csr1212_csr *csr) +{ + struct csr1212_csr_rom_cache *c, *oc; + struct csr1212_cache_region *cr, *ocr; + + csr1212_release_keyval(csr->root_kv); + + c = csr->cache_head; + while (c) { + oc = c; + cr = c->filled_head; + while (cr) { + ocr = cr; + cr = cr->next; + CSR1212_FREE(ocr); + } + c = c->next; + CSR1212_FREE(oc); + } + + CSR1212_FREE(csr); +} + + + +/* CSR Image Creation */ + +static int csr1212_append_new_cache(struct csr1212_csr *csr, size_t romsize) +{ + struct csr1212_csr_rom_cache *cache; + u_int64_t csr_addr; + + if (!csr || !csr->ops || !csr->ops->allocate_addr_range || + !csr->ops->release_addr || csr->max_rom < 1) + return CSR1212_EINVAL; + + /* ROM size must be a multiple of csr->max_rom */ + romsize = (romsize + (csr->max_rom - 1)) & ~(csr->max_rom - 1); + + csr_addr = csr->ops->allocate_addr_range(romsize, csr->max_rom, csr->private_data); + if (csr_addr == ~0ULL) { + return CSR1212_ENOMEM; + } + if (csr_addr < CSR1212_REGISTER_SPACE_BASE) { + /* Invalid address returned from allocate_addr_range(). */ + csr->ops->release_addr(csr_addr, csr->private_data); + return CSR1212_ENOMEM; + } + + cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE, romsize); + if (!cache) { + csr->ops->release_addr(csr_addr, csr->private_data); + return CSR1212_ENOMEM; + } + + cache->ext_rom = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, CSR1212_KV_ID_EXTENDED_ROM); + if (!cache->ext_rom) { + csr->ops->release_addr(csr_addr, csr->private_data); + CSR1212_FREE(cache); + return CSR1212_ENOMEM; + } + + if (csr1212_attach_keyval_to_directory(csr->root_kv, cache->ext_rom) != CSR1212_SUCCESS) { + csr1212_release_keyval(cache->ext_rom); + csr->ops->release_addr(csr_addr, csr->private_data); + CSR1212_FREE(cache); + return CSR1212_ENOMEM; + } + cache->ext_rom->offset = csr_addr - CSR1212_REGISTER_SPACE_BASE; + cache->ext_rom->value.leaf.len = -1; + cache->ext_rom->value.leaf.data = cache->data; + + /* Add cache to tail of cache list */ + cache->prev = csr->cache_tail; + csr->cache_tail->next = cache; + csr->cache_tail = cache; + return CSR1212_SUCCESS; +} + +static inline void csr1212_remove_cache(struct csr1212_csr *csr, + struct csr1212_csr_rom_cache *cache) +{ + if (csr->cache_head == cache) + csr->cache_head = cache->next; + if (csr->cache_tail == cache) + csr->cache_tail = cache->prev; + + if (cache->prev) + cache->prev->next = cache->next; + if (cache->next) + cache->next->prev = cache->prev; + + if (cache->ext_rom) { + csr1212_detach_keyval_from_directory(csr->root_kv, cache->ext_rom); + csr1212_release_keyval(cache->ext_rom); + } + + CSR1212_FREE(cache); +} + +static int csr1212_generate_layout_subdir(struct csr1212_keyval *dir, + struct csr1212_keyval **layout_tail) +{ + struct csr1212_dentry *dentry; + struct csr1212_keyval *dkv; + struct csr1212_keyval *last_extkey_spec = NULL; + struct csr1212_keyval *last_extkey = NULL; + int num_entries = 0; + + for (dentry = dir->value.directory.dentries_head; dentry; + dentry = dentry->next) { + for (dkv = dentry->kv; dkv; dkv = dkv->associate) { + /* Special Case: Extended Key Specifier_ID */ + if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { + if (last_extkey_spec == NULL) { + last_extkey_spec = dkv; + } else if (dkv->value.immediate != last_extkey_spec->value.immediate) { + last_extkey_spec = dkv; + } else { + continue; + } + /* Special Case: Extended Key */ + } else if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY) { + if (last_extkey == NULL) { + last_extkey = dkv; + } else if (dkv->value.immediate != last_extkey->value.immediate) { + last_extkey = dkv; + } else { + continue; + } + } + + num_entries += 1; + + switch(dkv->key.type) { + default: + case CSR1212_KV_TYPE_IMMEDIATE: + case CSR1212_KV_TYPE_CSR_OFFSET: + break; + case CSR1212_KV_TYPE_LEAF: + case CSR1212_KV_TYPE_DIRECTORY: + /* Remove from list */ + if (dkv->prev && (dkv->prev->next == dkv)) + dkv->prev->next = dkv->next; + if (dkv->next && (dkv->next->prev == dkv)) + dkv->next->prev = dkv->prev; + //if (dkv == *layout_tail) + // *layout_tail = dkv->prev; + + /* Special case: Extended ROM leafs */ + if (dkv->key.id == CSR1212_KV_ID_EXTENDED_ROM) { + dkv->value.leaf.len = -1; + /* Don't add Extended ROM leafs in the layout list, + * they are handled differently. */ + break; + } + + /* Add to tail of list */ + dkv->next = NULL; + dkv->prev = *layout_tail; + (*layout_tail)->next = dkv; + *layout_tail = dkv; + break; + } + } + } + return num_entries; +} + +size_t csr1212_generate_layout_order(struct csr1212_keyval *kv) +{ + struct csr1212_keyval *ltail = kv; + size_t agg_size = 0; + + while(kv) { + switch(kv->key.type) { + case CSR1212_KV_TYPE_LEAF: + /* Add 1 quadlet for crc/len field */ + agg_size += kv->value.leaf.len + 1; + break; + + case CSR1212_KV_TYPE_DIRECTORY: + kv->value.directory.len = csr1212_generate_layout_subdir(kv, <ail); + /* Add 1 quadlet for crc/len field */ + agg_size += kv->value.directory.len + 1; + break; + } + kv = kv->next; + } + return quads_to_bytes(agg_size); +} + +struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, + struct csr1212_keyval *start_kv, + int start_pos) +{ + struct csr1212_keyval *kv = start_kv; + struct csr1212_keyval *okv = start_kv; + int pos = start_pos; + int kv_len = 0, okv_len = 0; + + cache->layout_head = kv; + + while(kv && pos < cache->size) { + /* Special case: Extended ROM leafs */ + if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) { + kv->offset = cache->offset + pos; + } + + switch(kv->key.type) { + case CSR1212_KV_TYPE_LEAF: + kv_len = kv->value.leaf.len; + break; + + case CSR1212_KV_TYPE_DIRECTORY: + kv_len = kv->value.directory.len; + break; + + default: + /* Should never get here */ + break; + } + + pos += quads_to_bytes(kv_len + 1); + + if (pos <= cache->size) { + okv = kv; + okv_len = kv_len; + kv = kv->next; + } + } + + cache->layout_tail = okv; + cache->len = (okv->offset - cache->offset) + quads_to_bytes(okv_len + 1); + + return kv; +} + +static void csr1212_generate_tree_subdir(struct csr1212_keyval *dir, + u_int32_t *data_buffer) +{ + struct csr1212_dentry *dentry; + struct csr1212_keyval *last_extkey_spec = NULL; + struct csr1212_keyval *last_extkey = NULL; + int index = 0; + + for (dentry = dir->value.directory.dentries_head; dentry; dentry = dentry->next) { + struct csr1212_keyval *a; + + for (a = dentry->kv; a; a = a->associate) { + u_int32_t value = 0; + + /* Special Case: Extended Key Specifier_ID */ + if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { + if (last_extkey_spec == NULL) { + last_extkey_spec = a; + } else if (a->value.immediate != last_extkey_spec->value.immediate) { + last_extkey_spec = a; + } else { + continue; + } + /* Special Case: Extended Key */ + } else if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY) { + if (last_extkey == NULL) { + last_extkey = a; + } else if (a->value.immediate != last_extkey->value.immediate) { + last_extkey = a; + } else { + continue; + } + } + + switch(a->key.type) { + case CSR1212_KV_TYPE_IMMEDIATE: + value = a->value.immediate; + break; + case CSR1212_KV_TYPE_CSR_OFFSET: + value = a->value.csr_offset; + break; + case CSR1212_KV_TYPE_LEAF: + value = a->offset; + value -= dir->offset + quads_to_bytes(1+index); + value = bytes_to_quads(value); + break; + case CSR1212_KV_TYPE_DIRECTORY: + value = a->offset; + value -= dir->offset + quads_to_bytes(1+index); + value = bytes_to_quads(value); + break; + default: + /* Should never get here */ + break; /* GDB breakpoint */ + } + + value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) << CSR1212_KV_KEY_SHIFT; + value |= (a->key.type & CSR1212_KV_KEY_TYPE_MASK) << + (CSR1212_KV_KEY_SHIFT + CSR1212_KV_KEY_TYPE_SHIFT); + data_buffer[index] = CSR1212_CPU_TO_BE32(value); + index++; + } + } +} + +void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache) +{ + struct csr1212_keyval *kv, *nkv; + struct csr1212_keyval_img *kvi; + + for (kv = cache->layout_head; kv != cache->layout_tail->next; kv = nkv) { + kvi = (struct csr1212_keyval_img *) + (cache->data + bytes_to_quads(kv->offset - cache->offset)); + switch(kv->key.type) { + default: + case CSR1212_KV_TYPE_IMMEDIATE: + case CSR1212_KV_TYPE_CSR_OFFSET: + /* Should never get here */ + break; /* GDB breakpoint */ + + case CSR1212_KV_TYPE_LEAF: + /* Don't copy over Extended ROM areas, they are + * already filled out! */ + if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) + memcpy(kvi->data, kv->value.leaf.data, + quads_to_bytes(kv->value.leaf.len)); + + kvi->length = CSR1212_CPU_TO_BE16(kv->value.leaf.len); + kvi->crc = csr1212_crc16(kvi->data, kv->value.leaf.len); + break; + + case CSR1212_KV_TYPE_DIRECTORY: + csr1212_generate_tree_subdir(kv, kvi->data); + + kvi->length = CSR1212_CPU_TO_BE16(kv->value.directory.len); + kvi->crc = csr1212_crc16(kvi->data, kv->value.directory.len); + break; + } + + nkv = kv->next; + if (kv->prev) + kv->prev->next = NULL; + if (kv->next) + kv->next->prev = NULL; + kv->prev = NULL; + kv->next = NULL; + } +} + +int csr1212_generate_csr_image(struct csr1212_csr *csr) +{ + struct csr1212_bus_info_block_img *bi; + struct csr1212_csr_rom_cache *cache; + struct csr1212_keyval *kv; + size_t agg_size; + int ret; + int init_offset; + + if (!csr) + return CSR1212_EINVAL; + + cache = csr->cache_head; + + bi = (struct csr1212_bus_info_block_img*)cache->data; + + bi->length = bytes_to_quads(csr->bus_info_len) - 1; + bi->crc_length = bi->length; + bi->crc = csr1212_crc16(bi->data, bi->crc_length); + + csr->root_kv->next = NULL; + csr->root_kv->prev = NULL; + + agg_size = csr1212_generate_layout_order(csr->root_kv); + + init_offset = csr->bus_info_len; + + for (kv = csr->root_kv, cache = csr->cache_head; kv; cache = cache->next) { + if (!cache) { + /* Estimate approximate number of additional cache + * regions needed (it assumes that the cache holding + * the first 1K Config ROM space always exists). */ + int est_c = agg_size / (CSR1212_EXTENDED_ROM_SIZE - + (2 * sizeof(u_int32_t))) + 1; + + /* Add additional cache regions, extras will be + * removed later */ + for (; est_c; est_c--) { + ret = csr1212_append_new_cache(csr, CSR1212_EXTENDED_ROM_SIZE); + if (ret != CSR1212_SUCCESS) + return ret; + } + /* Need to re-layout for additional cache regions */ + agg_size = csr1212_generate_layout_order(csr->root_kv); + kv = csr->root_kv; + cache = csr->cache_head; + init_offset = csr->bus_info_len; + } + kv = csr1212_generate_positions(cache, kv, init_offset); + agg_size -= cache->len; + init_offset = sizeof(u_int32_t); + } + + /* Remove unused, excess cache regions */ + while (cache) { + struct csr1212_csr_rom_cache *oc = cache; + + cache = cache->next; + csr1212_remove_cache(csr, oc); + } + + /* Go through the list backward so that when done, the correct CRC + * will be calculated for the Extended ROM areas. */ + for(cache = csr->cache_tail; cache; cache = cache->prev) { + /* Only Extended ROM caches should have this set. */ + if (cache->ext_rom) { + int leaf_size; + + /* Make sure the Extended ROM leaf is a multiple of + * max_rom in size. */ + if (csr->max_rom < 1) + return CSR1212_EINVAL; + leaf_size = (cache->len + (csr->max_rom - 1)) & + ~(csr->max_rom - 1); + + /* Zero out the unused ROM region */ + memset(cache->data + bytes_to_quads(cache->len), 0x00, + leaf_size - cache->len); + + /* Subtract leaf header */ + leaf_size -= sizeof(u_int32_t); + + /* Update the Extended ROM leaf length */ + cache->ext_rom->value.leaf.len = + bytes_to_quads(leaf_size); + } else { + /* Zero out the unused ROM region */ + memset(cache->data + bytes_to_quads(cache->len), 0x00, + cache->size - cache->len); + } + + /* Copy the data into the cache buffer */ + csr1212_fill_cache(cache); + + if (cache != csr->cache_head) { + /* Set the length and CRC of the extended ROM. */ + struct csr1212_keyval_img *kvi = + (struct csr1212_keyval_img*)cache->data; + + kvi->length = CSR1212_CPU_TO_BE16(bytes_to_quads(cache->len) - 1); + kvi->crc = csr1212_crc16(kvi->data, + bytes_to_quads(cache->len) - 1); + + } + } + + return CSR1212_SUCCESS; +} + +int csr1212_read(struct csr1212_csr *csr, u_int32_t offset, void *buffer, u_int32_t len) +{ + struct csr1212_csr_rom_cache *cache; + + for (cache = csr->cache_head; cache; cache = cache->next) { + if (offset >= cache->offset && + (offset + len) <= (cache->offset + cache->size)) { + memcpy(buffer, + &cache->data[bytes_to_quads(offset - cache->offset)], + len); + return CSR1212_SUCCESS; + } + } + return CSR1212_ENOENT; +} + + + +/* Parse a chunk of data as a Config ROM */ + +static int csr1212_parse_bus_info_block(struct csr1212_csr *csr) +{ + struct csr1212_bus_info_block_img *bi; + struct csr1212_cache_region *cr; + int i; + int ret; + + /* IEEE 1212 says that the entire bus info block should be readable in + * a single transaction regardless of the max_rom value. + * Unfortunately, many IEEE 1394 devices do not abide by that, so the + * bus info block will be read 1 quadlet at a time. The rest of the + * ConfigROM will be read according to the max_rom field. */ + for (i = 0; i < csr->bus_info_len; i += sizeof(csr1212_quad_t)) { + ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, + sizeof(csr1212_quad_t), + &csr->cache_head->data[bytes_to_quads(i)], + csr->private_data); + if (ret != CSR1212_SUCCESS) + return ret; + } + + bi = (struct csr1212_bus_info_block_img*)csr->cache_head->data; + csr->crc_len = quads_to_bytes(bi->crc_length); + + /* IEEE 1212 recommends that crc_len be equal to bus_info_len, but that is not + * always the case, so read the rest of the crc area 1 quadlet at a time. */ + for (i = csr->bus_info_len; i <= csr->crc_len; i += sizeof(csr1212_quad_t)) { + ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, + sizeof(csr1212_quad_t), + &csr->cache_head->data[bytes_to_quads(i)], + csr->private_data); + if (ret != CSR1212_SUCCESS) + return ret; + } + + if (bytes_to_quads(csr->bus_info_len - sizeof(csr1212_quad_t)) != bi->length) + return CSR1212_EINVAL; + +#if 0 + /* Apparently there are too many differnt wrong implementations of the + * CRC algorithm that verifying them is moot. */ + if ((csr1212_crc16(bi->data, bi->crc_length) != bi->crc) && + (csr1212_msft_crc16(bi->data, bi->crc_length) != bi->crc)) + return CSR1212_EINVAL; +#endif + + cr = CSR1212_MALLOC(sizeof(struct csr1212_cache_region)); + if (!cr) + return CSR1212_ENOMEM; + + cr->next = NULL; + cr->prev = NULL; + cr->offset_start = 0; + cr->offset_end = csr->crc_len + 4; + + csr->cache_head->filled_head = cr; + csr->cache_head->filled_tail = cr; + + return CSR1212_SUCCESS; +} + +static int csr1212_parse_dir_entry(struct csr1212_keyval *dir, + csr1212_quad_t ki, + u_int32_t kv_pos) +{ + int ret = CSR1212_SUCCESS; + struct csr1212_keyval *k = NULL; + u_int32_t offset; + + switch(CSR1212_KV_KEY_TYPE(ki)) { + case CSR1212_KV_TYPE_IMMEDIATE: + k = csr1212_new_immediate(CSR1212_KV_KEY_ID(ki), + CSR1212_KV_VAL(ki)); + if (!k) { + ret = CSR1212_ENOMEM; + goto fail; + } + + k->refcnt = 0; /* Don't keep local reference when parsing. */ + break; + + case CSR1212_KV_TYPE_CSR_OFFSET: + k = csr1212_new_csr_offset(CSR1212_KV_KEY_ID(ki), + CSR1212_KV_VAL(ki)); + if (!k) { + ret = CSR1212_ENOMEM; + goto fail; + } + k->refcnt = 0; /* Don't keep local reference when parsing. */ + break; + + default: + /* Compute the offset from 0xffff f000 0000. */ + offset = quads_to_bytes(CSR1212_KV_VAL(ki)) + kv_pos; + if (offset == kv_pos) { + /* Uh-oh. Can't have a relative offset of 0 for Leaves + * or Directories. The Config ROM image is most likely + * messed up, so we'll just abort here. */ + ret = CSR1212_EIO; + goto fail; + } + + k = csr1212_find_keyval_offset(dir, offset); + + if (k) + break; /* Found it. */ + + if (CSR1212_KV_KEY_TYPE(ki) == CSR1212_KV_TYPE_DIRECTORY) { + k = csr1212_new_directory(CSR1212_KV_KEY_ID(ki)); + } else { + k = csr1212_new_leaf(CSR1212_KV_KEY_ID(ki), NULL, 0); + } + if (!k) { + ret = CSR1212_ENOMEM; + goto fail; + } + k->refcnt = 0; /* Don't keep local reference when parsing. */ + k->valid = 0; /* Contents not read yet so it's not valid. */ + k->offset = offset; + + k->prev = dir; + k->next = dir->next; + dir->next->prev = k; + dir->next = k; + } + ret = csr1212_attach_keyval_to_directory(dir, k); + +fail: + if (ret != CSR1212_SUCCESS) { + if (k) + free_keyval(k); + } + return ret; +} + + +int csr1212_parse_keyval(struct csr1212_keyval *kv, + struct csr1212_csr_rom_cache *cache) +{ + struct csr1212_keyval_img *kvi; + int i; + int ret = CSR1212_SUCCESS; + int kvi_len; + + kvi = (struct csr1212_keyval_img*)&cache->data[bytes_to_quads(kv->offset - + cache->offset)]; + kvi_len = CSR1212_BE16_TO_CPU(kvi->length); + +#if 0 + /* Apparently there are too many differnt wrong implementations of the + * CRC algorithm that verifying them is moot. */ + if ((csr1212_crc16(kvi->data, kvi_len) != kvi->crc) && + (csr1212_msft_crc16(kvi->data, kvi_len) != kvi->crc)) { + ret = CSR1212_EINVAL; + goto fail; + } +#endif + + switch(kv->key.type) { + case CSR1212_KV_TYPE_DIRECTORY: + for (i = 0; i < kvi_len; i++) { + csr1212_quad_t ki = kvi->data[i]; + + /* Some devices put null entries in their unit + * directories. If we come across such an entry, + * then skip it. */ + if (ki == 0x0) + continue; + ret = csr1212_parse_dir_entry(kv, ki, + (kv->offset + + quads_to_bytes(i + 1))); + } + kv->value.directory.len = kvi_len; + break; + + case CSR1212_KV_TYPE_LEAF: + if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) { + kv->value.leaf.data = CSR1212_MALLOC(quads_to_bytes(kvi_len)); + if (!kv->value.leaf.data) + { + ret = CSR1212_ENOMEM; + goto fail; + } + + kv->value.leaf.len = kvi_len; + memcpy(kv->value.leaf.data, kvi->data, quads_to_bytes(kvi_len)); + } + break; + } + + kv->valid = 1; + +fail: + return ret; +} + + +int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) +{ + struct csr1212_cache_region *cr, *ncr, *newcr = NULL; + struct csr1212_keyval_img *kvi = NULL; + struct csr1212_csr_rom_cache *cache; + int cache_index; + u_int64_t addr; + u_int32_t *cache_ptr; + u_int16_t kv_len = 0; + + if (!csr || !kv || csr->max_rom < 1) + return CSR1212_EINVAL; + + /* First find which cache the data should be in (or go in if not read + * yet). */ + for (cache = csr->cache_head; cache; cache = cache->next) { + if (kv->offset >= cache->offset && + kv->offset < (cache->offset + cache->size)) + break; + } + + if (!cache) { + csr1212_quad_t q; + u_int32_t cache_size; + + /* Only create a new cache for Extended ROM leaves. */ + if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) + return CSR1212_EINVAL; + + if (csr->ops->bus_read(csr, + CSR1212_REGISTER_SPACE_BASE + kv->offset, + sizeof(csr1212_quad_t), &q, csr->private_data)) { + return CSR1212_EIO; + } + + kv->value.leaf.len = CSR1212_BE32_TO_CPU(q) >> 16; + + cache_size = (quads_to_bytes(kv->value.leaf.len + 1) + + (csr->max_rom - 1)) & ~(csr->max_rom - 1); + + cache = csr1212_rom_cache_malloc(kv->offset, cache_size); + if (!cache) + return CSR1212_ENOMEM; + + kv->value.leaf.data = &cache->data[1]; + csr->cache_tail->next = cache; + cache->prev = csr->cache_tail; + cache->next = NULL; + csr->cache_tail = cache; + cache->filled_head = + CSR1212_MALLOC(sizeof(struct csr1212_cache_region)); + if (!cache->filled_head) { + return CSR1212_ENOMEM; + } + + cache->filled_head->offset_start = 0; + cache->filled_head->offset_end = sizeof(csr1212_quad_t); + cache->filled_tail = cache->filled_head; + cache->filled_head->next = NULL; + cache->filled_head->prev = NULL; + cache->data[0] = q; + + /* Don't read the entire extended ROM now. Pieces of it will + * be read when entries inside it are read. */ + return csr1212_parse_keyval(kv, cache); + } + + cache_index = kv->offset - cache->offset; + + /* Now seach read portions of the cache to see if it is there. */ + for (cr = cache->filled_head; cr; cr = cr->next) { + if (cache_index < cr->offset_start) { + newcr = CSR1212_MALLOC(sizeof(struct csr1212_cache_region)); + if (!newcr) + return CSR1212_ENOMEM; + + newcr->offset_start = cache_index & ~(csr->max_rom - 1); + newcr->offset_end = newcr->offset_start; + newcr->next = cr; + newcr->prev = cr->prev; + cr->prev = newcr; + cr = newcr; + break; + } else if ((cache_index >= cr->offset_start) && + (cache_index < cr->offset_end)) { + kvi = (struct csr1212_keyval_img*) + (&cache->data[bytes_to_quads(cache_index)]); + kv_len = quads_to_bytes(CSR1212_BE16_TO_CPU(kvi->length) + + 1); + break; + } else if (cache_index == cr->offset_end) + break; + } + + if (!cr) { + cr = cache->filled_tail; + newcr = CSR1212_MALLOC(sizeof(struct csr1212_cache_region)); + if (!newcr) + return CSR1212_ENOMEM; + + newcr->offset_start = cache_index & ~(csr->max_rom - 1); + newcr->offset_end = newcr->offset_start; + newcr->prev = cr; + newcr->next = cr->next; + cr->next = newcr; + cr = newcr; + cache->filled_tail = newcr; + } + + while(!kvi || cr->offset_end < cache_index + kv_len) { + cache_ptr = &cache->data[bytes_to_quads(cr->offset_end & + ~(csr->max_rom - 1))]; + + addr = (CSR1212_CSR_ARCH_REG_SPACE_BASE + cache->offset + + cr->offset_end) & ~(csr->max_rom - 1); + + if (csr->ops->bus_read(csr, addr, csr->max_rom, cache_ptr, + csr->private_data)) { + if (csr->max_rom == 4) + /* We've got problems! */ + return CSR1212_EIO; + + /* Apperently the max_rom value was a lie, set it to + * do quadlet reads and try again. */ + csr->max_rom = 4; + continue; + } + + cr->offset_end += csr->max_rom - (cr->offset_end & + (csr->max_rom - 1)); + + if (!kvi && (cr->offset_end > cache_index)) { + kvi = (struct csr1212_keyval_img*) + (&cache->data[bytes_to_quads(cache_index)]); + kv_len = quads_to_bytes(CSR1212_BE16_TO_CPU(kvi->length) + + 1); + } + + if ((kv_len + (kv->offset - cache->offset)) > cache->size) { + /* The Leaf or Directory claims its length extends + * beyond the ConfigROM image region and thus beyond the + * end of our cache region. Therefore, we abort now + * rather than seg faulting later. */ + return CSR1212_EIO; + } + + ncr = cr->next; + + if (ncr && (cr->offset_end >= ncr->offset_start)) { + /* consolidate region entries */ + ncr->offset_start = cr->offset_start; + + if (cr->prev) + cr->prev->next = cr->next; + ncr->prev = cr->prev; + if (cache->filled_head == cr) + cache->filled_head = ncr; + CSR1212_FREE(cr); + cr = ncr; + } + } + + return csr1212_parse_keyval(kv, cache); +} + + + +int csr1212_parse_csr(struct csr1212_csr *csr) +{ + static const int mr_map[] = { 4, 64, 1024, 0 }; + struct csr1212_dentry *dentry; + int ret; + + if (!csr || !csr->ops || !csr->ops->bus_read) + return CSR1212_EINVAL; + + ret = csr1212_parse_bus_info_block(csr); + if (ret != CSR1212_SUCCESS) + return ret; + + if (!csr->ops->get_max_rom) + csr->max_rom = mr_map[0]; /* default value */ + else { + int i = csr->ops->get_max_rom(csr->bus_info_data, + csr->private_data); + if (i & ~0x3) + return CSR1212_EINVAL; + csr->max_rom = mr_map[i]; + } + + csr->cache_head->layout_head = csr->root_kv; + csr->cache_head->layout_tail = csr->root_kv; + + csr->root_kv->offset = (CSR1212_CONFIG_ROM_SPACE_BASE & 0xffff) + + csr->bus_info_len; + + csr->root_kv->valid = 0; + csr->root_kv->next = csr->root_kv; + csr->root_kv->prev = csr->root_kv; + csr1212_get_keyval(csr, csr->root_kv); + + /* Scan through the Root directory finding all extended ROM regions + * and make cache regions for them */ + for (dentry = csr->root_kv->value.directory.dentries_head; + dentry; dentry = dentry->next) { + if (dentry->kv->key.id == CSR1212_KV_ID_EXTENDED_ROM) { + csr1212_get_keyval(csr, dentry->kv); + + if (ret != CSR1212_SUCCESS) + return ret; + } + } + + return CSR1212_SUCCESS; +} Index: /branches/libffado-2.0/src/libieee1394/IsoHandler.h =================================================================== --- /branches/libffado-2.0/src/libieee1394/IsoHandler.h (revision 1038) +++ /branches/libffado-2.0/src/libieee1394/IsoHandler.h (revision 1038) @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_ISOHANDLER__ +#define __FFADO_ISOHANDLER__ + +#include "debugmodule/debugmodule.h" + +#include "libutil/Thread.h" + +enum raw1394_iso_disposition; + +class IsoHandlerManager; +namespace Streaming { + class StreamProcessor; +} + +/*! +\brief The Base Class for ISO Handlers + + These classes perform the actual ISO communication through libraw1394. + They are different from Streaming::StreamProcessors because one handler can provide multiple + streams with packets in case of ISO multichannel receive. + +*/ + +class IsoHandler +{ +public: + enum EHandlerType { + eHT_Receive, + eHT_Transmit + }; + IsoHandler(IsoHandlerManager& manager, enum EHandlerType t); + IsoHandler(IsoHandlerManager& manager, enum EHandlerType t, + unsigned int buf_packets, unsigned int max_packet_size, int irq); + IsoHandler(IsoHandlerManager& manager, enum EHandlerType t, + unsigned int buf_packets, unsigned int max_packet_size, int irq, enum raw1394_iso_speed speed); + ~IsoHandler(); + +private: // the ISO callback interface + static enum raw1394_iso_disposition + iso_receive_handler(raw1394handle_t handle, unsigned char *data, + unsigned int length, unsigned char channel, + unsigned char tag, unsigned char sy, unsigned int cycle, + unsigned int dropped); + + enum raw1394_iso_disposition + putPacket(unsigned char *data, unsigned int length, + unsigned char channel, unsigned char tag, unsigned char sy, + unsigned int cycle, unsigned int dropped, unsigned int skipped); + + static enum raw1394_iso_disposition iso_transmit_handler(raw1394handle_t handle, + unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped); + enum raw1394_iso_disposition + getPacket(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int skipped); + +public: + // runnable interface + bool iterate(); + + int getFileDescriptor() { return raw1394_get_fd(m_handle);}; + + bool init(); + bool prepare(); + + void setVerboseLevel(int l); + + bool enable() {return enable(-1);}; + bool enable(int cycle); + bool disable(); + + void flush(); + enum EHandlerType getType() {return m_type;}; + const char *getTypeString() {return eHTToString(m_type); }; + + // pretty printing + const char *eHTToString(enum EHandlerType); + + bool isEnabled() + {return m_State == E_Running;}; + + // no setter functions, because those would require a re-init + unsigned int getMaxPacketSize() { return m_max_packet_size;}; + unsigned int getNbBuffers() { return m_buf_packets;}; + int getPacketLatency() { return m_irq_interval;}; + + unsigned int getPreBuffers() {return m_prebuffers;}; + void setPreBuffers(unsigned int n) {m_prebuffers=n;}; + + void dumpInfo(); + + bool inUse() {return (m_Client != 0) ;}; + bool isStreamRegistered(Streaming::StreamProcessor *s) {return (m_Client == s);}; + + bool registerStream(Streaming::StreamProcessor *); + bool unregisterStream(Streaming::StreamProcessor *); + + bool canIterateClient(); // FIXME: implement with functor + + /** + * @brief request that the handler exits the packet processing loop ASAP + * + * The raw1394 lib doesn't provide a means to stop the packet iteration loop + * except when the iterate callback returns a DEFER value. Calling this function + * will make the callback return DEFER ASAP. + */ + void requestIterateLoopExit() {m_dont_exit_iterate_loop = false;}; + /** + * @brief allow the handler to stay in the packet processing loop + * + * This resets the state set by requestIterateLoopExit() + */ + void allowIterateLoop() {m_dont_exit_iterate_loop = true;}; + + + /** + * @brief get last cycle number seen by handler + * @return cycle number + */ + int getLastCycle() {return m_last_cycle;}; + +private: + IsoHandlerManager& m_manager; + enum EHandlerType m_type; + raw1394handle_t m_handle; + unsigned int m_buf_packets; + unsigned int m_max_packet_size; + int m_irq_interval; + int m_last_cycle; + uint32_t m_last_now; + + Streaming::StreamProcessor *m_Client; // FIXME: implement with functors + + int handleBusReset(unsigned int generation); + + static int busreset_handler(raw1394handle_t handle, unsigned int generation); + + enum raw1394_iso_speed m_speed; + unsigned int m_prebuffers; + bool m_dont_exit_iterate_loop; + + // the state machine + enum EHandlerStates { + E_Created, + E_Initialized, + E_Prepared, + E_Running, + E_Error + }; + enum EHandlerStates m_State; + + #ifdef DEBUG + unsigned int m_packets; + unsigned int m_dropped; + int m_min_ahead; + #endif + + DECLARE_DEBUG_MODULE; +}; + +#endif /* __FFADO_ISOHANDLER__ */ + + + Index: /branches/libffado-2.0/src/ffadodevice.cpp =================================================================== --- /branches/libffado-2.0/src/ffadodevice.cpp (revision 1158) +++ /branches/libffado-2.0/src/ffadodevice.cpp (revision 1158) @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2005-2008 by Daniel Wagner + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "ffadodevice.h" +#include "devicemanager.h" + +#include "libieee1394/configrom.h" +#include "libieee1394/ieee1394service.h" + +#include "libcontrol/Element.h" +#include "libcontrol/ClockSelect.h" +#include "libcontrol/Nickname.h" + +#include +#include + +#include + +IMPL_DEBUG_MODULE( FFADODevice, FFADODevice, DEBUG_LEVEL_NORMAL ); + +FFADODevice::FFADODevice( DeviceManager& d, std::auto_ptr( configRom ) ) + : Control::Container(&d) + , m_pConfigRom( configRom ) + , m_pDeviceManager( d ) +{ + addOption(Util::OptionContainer::Option("id",std::string("dev?"))); + + std::ostringstream nodestr; + nodestr << "node" << getConfigRom().getNodeId(); + + if (!addElement(&getConfigRom())) { + debugWarning("failed to add ConfigRom to Control::Container\n"); + } + + m_genericContainer = new Control::Container(this, "Generic"); + if(m_genericContainer == NULL) { + debugError("Could not create Control::Container for generic controls\n"); + } else { + + if (!addElement(m_genericContainer)) { + debugWarning("failed to add generic container to Control::Container\n"); + } + // add a generic control for the clock source selection + if(!m_genericContainer->addElement(new Control::ClockSelect(*this))) { + debugWarning("failed to add clock source control to container\n"); + } + // add a generic control for the nickname + if(!m_genericContainer->addElement(new Control::Nickname(*this))) { + debugWarning("failed to add Nickname control to container\n"); + } + } +} + +FFADODevice::~FFADODevice() +{ + if (!deleteElement(&getConfigRom())) { + debugWarning("failed to remove ConfigRom from Control::Container\n"); + } + + // remove generic controls if present + if(m_genericContainer) { + if (!deleteElement(m_genericContainer)) { + debugError("Generic controls present but not registered to the avdevice\n"); + } + // remove and delete (as in free) child control elements + m_genericContainer->clearElements(true); + delete m_genericContainer; + } +} + +FFADODevice * +FFADODevice::createDevice(std::auto_ptr( x )) +{ + // re-implement this!! + assert(0); + return NULL; +} + +std::string +FFADODevice::getName() +{ + return getConfigRom().getGuidString(); +} + +int +FFADODevice::getNodeId() +{ + return getConfigRom().getNodeId(); +} + +bool FFADODevice::compareGUID( FFADODevice *a, FFADODevice *b ) { + assert(a); + assert(b); + return ConfigRom::compareGUID(a->getConfigRom(), b->getConfigRom()); +} + +ConfigRom& +FFADODevice::getConfigRom() const +{ + return *m_pConfigRom; +} + +Ieee1394Service& +FFADODevice::get1394Service() +{ + return getConfigRom().get1394Service(); +} + +bool +FFADODevice::loadFromCache() +{ + return false; +} + +bool +FFADODevice::saveCache() +{ + return false; +} + +enum FFADODevice::eSyncState +FFADODevice::getSyncState( ) { + return eSS_Unknown; +} + +bool +FFADODevice::setId( unsigned int id) +{ + Util::MutexLockHelper lock(m_DeviceMutex); + bool retval; + // FIXME: decent ID system nescessary + std::ostringstream idstr; + idstr << "dev" << id; + debugOutput( DEBUG_LEVEL_VERBOSE, "Set id to %s...\n", idstr.str().c_str()); + + retval=setOption("id",idstr.str()); + return retval; +} + +bool +FFADODevice::setNickname( std::string name) +{ + return false; +} + +std::string +FFADODevice::getNickname() +{ + return "Unknown"; +} + +void +FFADODevice::handleBusReset() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Handle bus reset...\n"); + + // update the config rom node id + sleep(1); + + Util::MutexLockHelper lock(m_DeviceMutex); + getConfigRom().setVerboseLevel(getDebugLevel()); + getConfigRom().updatedNodeId(); +} + +void +FFADODevice::setVerboseLevel(int l) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); + setDebugLevel(l); + m_DeviceMutex.setVerboseLevel(l); + getConfigRom().setVerboseLevel(l); +} + +void +FFADODevice::showDevice() +{ + #ifdef DEBUG + Ieee1394Service& s = getConfigRom().get1394Service(); + debugOutput(DEBUG_LEVEL_NORMAL, "Attached to port.......: %d (%s)\n", + s.getPort(), s.getPortName().c_str()); + debugOutput(DEBUG_LEVEL_NORMAL, "Node...................: %d\n", getNodeId()); + debugOutput(DEBUG_LEVEL_NORMAL, "Vendor name............: %s\n", + getConfigRom().getVendorName().c_str()); + debugOutput(DEBUG_LEVEL_NORMAL, "Model name.............: %s\n", + getConfigRom().getModelName().c_str()); + debugOutput(DEBUG_LEVEL_NORMAL, "GUID...................: %s\n", + getConfigRom().getGuidString().c_str()); + + std::string id=std::string("dev? [none]"); + getOption("id", id); + + debugOutput(DEBUG_LEVEL_NORMAL, "Assigned ID....: %s\n", id.c_str()); + + flushDebugOutput(); + #endif +} + + +bool +FFADODevice::enableStreaming() { + return true; +} + +bool +FFADODevice::disableStreaming() { + return true; +} + +const char * +FFADODevice::ClockSourceTypeToString(enum eClockSourceType t) +{ + switch(t) { + default: return "Erratic type "; + case eCT_Invalid: return "Invalid "; + case eCT_Internal: return "Internal "; + case eCT_1394Bus: return "1394 Bus "; + case eCT_SytMatch: return "Compound Syt Match"; + case eCT_SytStream: return "Sync Syt Match "; + case eCT_WordClock: return "WordClock "; + case eCT_SPDIF: return "SPDIF "; + case eCT_ADAT: return "ADAT "; + case eCT_TDIF: return "TDIF "; + case eCT_AES: return "AES "; + } +} Index: /branches/libffado-2.0/src/devicemanager.h =================================================================== --- /branches/libffado-2.0/src/devicemanager.h (revision 1163) +++ /branches/libffado-2.0/src/devicemanager.h (revision 1163) @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2005-2008 by Daniel Wagner + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef FFADODEVICEMANAGER_H +#define FFADODEVICEMANAGER_H + +#include "debugmodule/debugmodule.h" + +#include "libieee1394/configrom.h" +#include "libieee1394/ieee1394service.h" + +#include "libstreaming/StreamProcessorManager.h" + +#include "libutil/OptionContainer.h" +#include "libcontrol/BasicElements.h" + +#include "libutil/Functors.h" +#include "libutil/Mutex.h" + +#include +#include + +class Ieee1394Service; +class FFADODevice; +class DeviceStringParser; + +namespace Streaming { + class StreamProcessor; +} + +typedef std::vector< FFADODevice* > FFADODeviceVector; +typedef std::vector< FFADODevice* >::iterator FFADODeviceVectorIterator; + +typedef std::vector< Ieee1394Service* > Ieee1394ServiceVector; +typedef std::vector< Ieee1394Service* >::iterator Ieee1394ServiceVectorIterator; + +typedef std::vector< Util::Functor* > FunctorVector; +typedef std::vector< Util::Functor* >::iterator FunctorVectorIterator; + +typedef std::vector< ConfigRom* > ConfigRomVector; +typedef std::vector< ConfigRom* >::iterator ConfigRomVectorIterator; + +class DeviceManager + : public Util::OptionContainer, + public Control::Container +{ +public: + enum eWaitResult { + eWR_OK, + eWR_Xrun, + eWR_Error, + eWR_Shutdown, + }; + + DeviceManager(); + ~DeviceManager(); + + bool setThreadParameters(bool rt, int priority); + + bool initialize(); + bool deinitialize(); + + bool addSpecString(char *); + bool isSpecStringValid(std::string s); + + bool discover(bool useCache=true, bool rediscover=false); + bool initStreaming(); + bool prepareStreaming(); + bool finishStreaming(); + bool startStreaming(); + bool stopStreaming(); + bool resetStreaming(); + enum eWaitResult waitForPeriod(); + bool setStreamingParams(unsigned int period, unsigned int rate, unsigned int nb_buffers); + + bool isValidNode( int node ); + int getNbDevices(); + int getDeviceNodeId( int deviceNr ); + + FFADODevice* getAvDevice( int nodeId ); + FFADODevice* getAvDeviceByIndex( int idx ); + unsigned int getAvDeviceCount(); + + Streaming::StreamProcessor *getSyncSource(); + + void ignoreBusResets(bool b) {m_ignore_busreset = b;}; + bool registerBusresetNotification(Util::Functor *f) + {return registerNotification(m_busResetNotifiers, f);}; + bool unregisterBusresetNotification(Util::Functor *f) + {return unregisterNotification(m_busResetNotifiers, f);}; + + bool registerPreUpdateNotification(Util::Functor *f) + {return registerNotification(m_preUpdateNotifiers, f);}; + bool unregisterPreUpdateNotification(Util::Functor *f) + {return unregisterNotification(m_preUpdateNotifiers, f);}; + + bool registerPostUpdateNotification(Util::Functor *f) + {return registerNotification(m_postUpdateNotifiers, f);}; + bool unregisterPostUpdateNotification(Util::Functor *f) + {return unregisterNotification(m_postUpdateNotifiers, f);}; + + void showDeviceInfo(); + void showStreamingInfo(); + + // the Control::Container functions + virtual std::string getName() + {return "DeviceManager";}; + virtual bool setName( std::string n ) + { return false;}; + +protected: + FFADODevice* getDriverForDevice( std::auto_ptr( configRom ), + int id ); + FFADODevice* getSlaveDriver( std::auto_ptr( configRom ) ); + + void busresetHandler(); + +protected: + // we have one service for each port + // found on the system. We don't allow dynamic addition of ports (yet) + Ieee1394ServiceVector m_1394Services; + FFADODeviceVector m_avDevices; + FunctorVector m_busreset_functors; + + // the lock protecting the device list + Util::Mutex* m_avDevicesLock; + // the lock to serialize bus reset handling + Util::Mutex* m_BusResetLock; + +public: // FIXME: this should be better + Streaming::StreamProcessorManager& getStreamProcessorManager() + {return *m_processorManager;}; +private: + Streaming::StreamProcessorManager* m_processorManager; + DeviceStringParser* m_deviceStringParser; + bool m_used_cache_last_time; + bool m_ignore_busreset; + + typedef std::vector< Util::Functor* > notif_vec_t; + notif_vec_t m_busResetNotifiers; + notif_vec_t m_preUpdateNotifiers; + notif_vec_t m_postUpdateNotifiers; + + bool registerNotification(notif_vec_t&, Util::Functor *); + bool unregisterNotification(notif_vec_t&, Util::Functor *); + void signalNotifiers(notif_vec_t& list); + +protected: + std::vector m_SpecStrings; + + bool m_thread_realtime; + int m_thread_priority; + +// debug stuff +public: + void setVerboseLevel(int l); +private: + DECLARE_DEBUG_MODULE; +}; + +#endif Index: /branches/libffado-2.0/src/libstreaming/motu/MotuPort.h =================================================================== --- /branches/libffado-2.0/src/libstreaming/motu/MotuPort.h (revision 864) +++ /branches/libffado-2.0/src/libstreaming/motu/MotuPort.h (revision 864) @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2005-2008 by Jonathan Woithe + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_MOTUPORT__ +#define __FFADO_MOTUPORT__ + +/** + * This file implements the ports used in Motu devices + */ + +#include "MotuPortInfo.h" +#include "../generic/Port.h" + +#include "debugmodule/debugmodule.h" + +namespace Streaming { + +/*! +\brief The Base Class for Motu Audio Port + + +*/ +class MotuAudioPort + : public AudioPort, public MotuPortInfo +{ + +public: + + MotuAudioPort(PortManager &m, + std::string name, + enum E_Direction direction, + int position, + int size) + : AudioPort(m, name, direction), + MotuPortInfo( position, size) // TODO: add more port information parameters here if nescessary + {}; + + virtual ~MotuAudioPort() {}; +}; + +/*! +\brief The Base Class for an Motu Midi Port + + +*/ +class MotuMidiPort + : public MidiPort, public MotuPortInfo +{ + +public: + + MotuMidiPort(PortManager &m, + std::string name, + enum E_Direction direction, + int position) + : MidiPort(m, name, direction), + MotuPortInfo(position, 0) // TODO: add more port information parameters here if nescessary + {}; + + virtual ~MotuMidiPort() {}; +}; + +/*! +\brief The Base Class for an Motu Control Port + + +*/ +class MotuControlPort + : public ControlPort, public MotuPortInfo +{ + +public: + + MotuControlPort(PortManager &m, + std::string name, + enum E_Direction direction, + int position) + : ControlPort(m, name, direction), + MotuPortInfo(position, 2) // TODO: add more port information parameters here if nescessary + {}; + + virtual ~MotuControlPort() {}; +}; + +} // end of namespace Streaming + +#endif /* __FFADO_MOTUPORT__ */ + Index: /branches/libffado-2.0/src/libstreaming/motu/MotuTransmitStreamProcessor.cpp =================================================================== --- /branches/libffado-2.0/src/libstreaming/motu/MotuTransmitStreamProcessor.cpp (revision 1168) +++ /branches/libffado-2.0/src/libstreaming/motu/MotuTransmitStreamProcessor.cpp (revision 1168) @@ -0,0 +1,754 @@ +/* + * Copyright (C) 2005-2008 by Jonathan Woithe + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "config.h" +#include "libutil/float_cast.h" + +#include "MotuTransmitStreamProcessor.h" +#include "MotuPort.h" +#include "../StreamProcessorManager.h" +#include "devicemanager.h" + +#include "libieee1394/ieee1394service.h" +#include "libieee1394/IsoHandlerManager.h" +#include "libieee1394/cycletimer.h" + +#include "libutil/ByteSwap.h" +#include + +// Set to 1 to enable the generation of a 1 kHz test tone in analog output 1. Even with +// this defined to 1 the test tone will now only be produced if run with a non-zero +// debug level. +#define TESTTONE 1 + +#if TESTTONE +#include +#endif + +/* Provide more intuitive access to GCC's branch predition built-ins */ +#define likely(x) __builtin_expect((x),1) +#define unlikely(x) __builtin_expect((x),0) + +namespace Streaming +{ + +// A macro to extract specific bits from a native endian quadlet +#define get_bits(_d,_start,_len) (((_d)>>((_start)-(_len)+1)) & ((1<<(_len))-1)) + +// Convert a full timestamp into an SPH timestamp as required by the MOTU +static inline uint32_t fullTicksToSph(int64_t timestamp) { + return TICKS_TO_CYCLE_TIMER(timestamp) & 0x1ffffff; +} + +/* transmit */ +MotuTransmitStreamProcessor::MotuTransmitStreamProcessor(FFADODevice &parent, unsigned int event_size ) + : StreamProcessor(parent, ePT_Transmit ) + , m_event_size( event_size ) + , m_tx_dbc( 0 ) + , mb_head( 0 ) + , mb_tail( 0 ) + , midi_lock( 0 ) +{ + int srate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate(); + /* Work out how many audio samples should be left between MIDI data bytes in order + * to stay under the MIDI hardware baud rate of 31250. MIDI data is transmitted + * using 10 bits per byte (including the start/stop bit) so this gives us 3125 bytes + * per second. If we send to the MOTU at a faster rate than this, some MIDI bytes + * will be dropped or corrupted in interesting ways. + */ + midi_tx_period = lrintf(ceil((float)srate / 3125)); +} + +unsigned int +MotuTransmitStreamProcessor::getMaxPacketSize() { + int framerate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate(); + return framerate<=48000?616:(framerate<=96000?1032:1160); +} + +unsigned int +MotuTransmitStreamProcessor::getNominalFramesPerPacket() { + int framerate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate(); + return framerate<=48000?8:(framerate<=96000?16:32); +} + +enum StreamProcessor::eChildReturnValue +MotuTransmitStreamProcessor::generatePacketHeader ( + unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr ) +{ + unsigned int cycle = CYCLE_TIMER_GET_CYCLES(pkt_ctr); + + // The number of events per packet expected by the MOTU is solely + // dependent on the current sample rate. An 'event' is one sample from + // all channels plus possibly other midi and control data. + signed n_events = getNominalFramesPerPacket(); + + // Do housekeeping expected for all packets sent to the MOTU, even + // for packets containing no audio data. + *sy = 0x00; + *tag = 1; // All MOTU packets have a CIP-like header + *length = n_events*m_event_size + 8; + + signed int fc; + uint64_t presentation_time; + unsigned int presentation_cycle; + int cycles_until_presentation; + + uint64_t transmit_at_time; + unsigned int transmit_at_cycle; + int cycles_until_transmit; + + debugOutput ( DEBUG_LEVEL_ULTRA_VERBOSE, "Try for cycle %d\n", cycle ); + // check whether the packet buffer has packets for us to send. + // the base timestamp is the one of the next sample in the buffer + ffado_timestamp_t ts_head_tmp; + m_data_buffer->getBufferHeadTimestamp ( &ts_head_tmp, &fc ); // thread safe + + // the timestamp gives us the time at which we want the sample block + // to be output by the device + presentation_time = ( uint64_t ) ts_head_tmp; + + // now we calculate the time when we have to transmit the sample block + transmit_at_time = substractTicks ( presentation_time, MOTU_TRANSMIT_TRANSFER_DELAY ); + + // calculate the cycle this block should be presented in + // (this is just a virtual calculation since at that time it should + // already be in the device's buffer) + presentation_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( presentation_time ) ); + + // calculate the cycle this block should be transmitted in + transmit_at_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( transmit_at_time ) ); + + // we can check whether this cycle is within the 'window' we have + // to send this packet. + // first calculate the number of cycles left before presentation time + cycles_until_presentation = diffCycles ( presentation_cycle, cycle ); + + // we can check whether this cycle is within the 'window' we have + // to send this packet. + // first calculate the number of cycles left before presentation time + cycles_until_transmit = diffCycles ( transmit_at_cycle, cycle ); + + // two different options: + // 1) there are not enough frames for one packet + // => determine wether this is a problem, since we might still + // have some time to send it + // 2) there are enough packets + // => determine whether we have to send them in this packet + if ( fc < ( signed int ) getNominalFramesPerPacket() ) + { + // not enough frames in the buffer, + + // we can still postpone the queueing of the packets + // if we are far enough ahead of the presentation time + if ( cycles_until_presentation <= MOTU_MIN_CYCLES_BEFORE_PRESENTATION ) + { + debugOutput ( DEBUG_LEVEL_VERBOSE, + "Insufficient frames (P): N=%02d, CY=%04u, TC=%04u, CUT=%04d\n", + fc, cycle, transmit_at_cycle, cycles_until_transmit ); + // we are too late + return eCRV_XRun; + } + else + { + debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, + "Insufficient frames (NP): N=%02d, CY=%04u, TC=%04u, CUT=%04d\n", + fc, cycle, transmit_at_cycle, cycles_until_transmit ); + // there is still time left to send the packet + // we want the system to give this packet another go at a later time instant + return eCRV_Again; + } + } + else + { + // there are enough frames, so check the time they are intended for + // all frames have a certain 'time window' in which they can be sent + // this corresponds to the range of the timestamp mechanism: + // we can send a packet 15 cycles in advance of the 'presentation time' + // in theory we can send the packet up till one cycle before the presentation time, + // however this is not very smart. + + // There are 3 options: + // 1) the frame block is too early + // => send an empty packet + // 2) the frame block is within the window + // => send it + // 3) the frame block is too late + // => discard (and raise xrun?) + // get next block of frames and repeat + + if(cycles_until_transmit < 0) + { + // we are too late + debugOutput(DEBUG_LEVEL_VERBOSE, + "Too late: CY=%04u, TC=%04u, CUT=%04d, TSP=%011llu (%04u)\n", + cycle, + transmit_at_cycle, cycles_until_transmit, + presentation_time, (unsigned int)TICKS_TO_CYCLES(presentation_time) ); + + // however, if we can send this sufficiently before the presentation + // time, it could be harmless. + // NOTE: dangerous since the device has no way of reporting that it didn't get + // this packet on time. + if(cycles_until_presentation >= MOTU_MIN_CYCLES_BEFORE_PRESENTATION) + { + // we are not that late and can still try to transmit the packet + m_tx_dbc += fillDataPacketHeader((quadlet_t *)data, length, presentation_time); + m_last_timestamp = presentation_time; + if (m_tx_dbc > 0xff) + m_tx_dbc -= 0x100; + return eCRV_Packet; + } + else // definitely too late + { + return eCRV_XRun; + } + } + else if(cycles_until_transmit <= MOTU_MAX_CYCLES_TO_TRANSMIT_EARLY) + { + // it's time send the packet + m_tx_dbc += fillDataPacketHeader((quadlet_t *)data, length, presentation_time); + m_last_timestamp = presentation_time; + if (m_tx_dbc > 0xff) + m_tx_dbc -= 0x100; + return eCRV_Packet; + } + else + { + debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, + "Too early: CY=%04u, TC=%04u, CUT=%04d, TST=%011llu (%04u), TSP=%011llu (%04u)\n", + cycle, + transmit_at_cycle, cycles_until_transmit, + transmit_at_time, ( unsigned int ) TICKS_TO_CYCLES ( transmit_at_time ), + presentation_time, ( unsigned int ) TICKS_TO_CYCLES ( presentation_time ) ); +#ifdef DEBUG + if ( cycles_until_transmit > MOTU_MAX_CYCLES_TO_TRANSMIT_EARLY + 1 ) + { + debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, + "Way too early: CY=%04u, TC=%04u, CUT=%04d, TST=%011llu (%04u), TSP=%011llu (%04u)\n", + cycle, + transmit_at_cycle, cycles_until_transmit, + transmit_at_time, ( unsigned int ) TICKS_TO_CYCLES ( transmit_at_time ), + presentation_time, ( unsigned int ) TICKS_TO_CYCLES ( presentation_time ) ); + } +#endif + // we are too early, send only an empty packet + return eCRV_EmptyPacket; + } + } + return eCRV_Invalid; +} + +enum StreamProcessor::eChildReturnValue +MotuTransmitStreamProcessor::generatePacketData ( + unsigned char *data, unsigned int *length) +{ + quadlet_t *quadlet = (quadlet_t *)data; + quadlet += 2; // skip the header + // Size of a single data frame in quadlets + unsigned dbs = m_event_size / 4; + + // The number of events per packet expected by the MOTU is solely + // dependent on the current sample rate. An 'event' is one sample from + // all channels plus possibly other midi and control data. + signed n_events = getNominalFramesPerPacket(); + + if (m_data_buffer->readFrames(n_events, (char *)(data + 8))) { + float ticks_per_frame = m_Parent.getDeviceManager().getStreamProcessorManager().getSyncSource().getTicksPerFrame(); + +#if TESTTONE + /* Now things are beginning to stabilise, make things easier for others by only playing + * the test tone when run with a non-zero debug level. + */ + if (getDebugLevel() > 0) { + // FIXME: remove this hacked in 1 kHz test signal to + // analog-1 when testing is complete. + signed int i, int_tpf = lrintf(ticks_per_frame); + unsigned char *sample = data+8+16; + for (i=0; i= 24576000) { + a_cx -= 24576000; + } + } + } +#endif + +//fprintf(stderr,"tx: %d/%d\n", +// TICKS_TO_CYCLES(fullTicksToSph(m_last_timestamp)), +// TICKS_TO_OFFSET(fullTicksToSph(m_last_timestamp))); + // Set up each frames's SPH. +//fprintf(stderr,"tpf=%f\n", ticks_per_frame); + for (int i=0; i < n_events; i++, quadlet += dbs) { + int64_t ts_frame = addTicks(m_last_timestamp, (unsigned int)lrintf(i * ticks_per_frame)); + *quadlet = CondSwapToBus32(fullTicksToSph(ts_frame)); +//fprintf(stderr,"tx: %d/%d\n", +// CYCLE_TIMER_GET_CYCLES(fullTicksToSph(ts_frame)), +// CYCLE_TIMER_GET_OFFSET(fullTicksToSph(ts_frame))); + } + + return eCRV_OK; + } + else return eCRV_XRun; + +} + +enum StreamProcessor::eChildReturnValue +MotuTransmitStreamProcessor::generateEmptyPacketHeader ( + unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr ) +{ + debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "XMIT EMPTY: CY=%04u, TSP=%011llu (%04u)\n", + CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp, + ( unsigned int ) TICKS_TO_CYCLES ( m_last_timestamp ) ); + + // Do housekeeping expected for all packets sent to the MOTU, even + // for packets containing no audio data. + *sy = 0x00; + *tag = 1; // All MOTU packets have a CIP-like header + *length = 8; + + m_tx_dbc += fillNoDataPacketHeader ( (quadlet_t *)data, length ); + return eCRV_OK; +} + +enum StreamProcessor::eChildReturnValue +MotuTransmitStreamProcessor::generateEmptyPacketData ( + unsigned char *data, unsigned int *length) +{ + return eCRV_OK; // no need to do anything +} + +enum StreamProcessor::eChildReturnValue +MotuTransmitStreamProcessor::generateSilentPacketHeader ( + unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr ) +{ + unsigned int cycle = CYCLE_TIMER_GET_CYCLES(pkt_ctr); + + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "XMIT SILENT: CY=%04u, TSP=%011llu (%04u)\n", + cycle, m_last_timestamp, + ( unsigned int ) TICKS_TO_CYCLES ( m_last_timestamp ) ); + + // A "silent" packet is identical to a regular data packet except all + // audio data is set to zero. The MOTU expects valid timestamps and + // rate control in silent packets, so much of the timing logic from + // generatePacketHeader() is needed here too - the main difference being + // the source of the packet timestamp. + + // The number of events per packet expected by the MOTU is solely + // dependent on the current sample rate. An 'event' is one sample from + // all channels plus possibly other midi and control data. + signed n_events = getNominalFramesPerPacket(); + + // Do housekeeping expected for all packets sent to the MOTU, even + // for packets containing no audio data. + *sy = 0x00; + *tag = 1; // All MOTU packets have a CIP-like header + + /* Assume the packet will have audio data. If it turns out we need an empty packet + * the length will be overridden by fillNoDataPacketHeader(). + */ + *length = n_events*m_event_size + 8; + + uint64_t presentation_time; + unsigned int presentation_cycle; + int cycles_until_presentation; + + uint64_t transmit_at_time; + unsigned int transmit_at_cycle; + int cycles_until_transmit; + + /* The sample buffer is not necessarily running when silent packets are + * needed, so use m_last_timestamp (the timestamp of the previously sent + * data packet) as the basis for the presentation time of the next + * packet. Since we're only writing zeros we don't have to deal with + * buffer xruns. + */ + float ticks_per_frame = m_Parent.getDeviceManager().getStreamProcessorManager().getSyncSource().getTicksPerFrame(); + presentation_time = addTicks(m_last_timestamp, (unsigned int)lrintf(n_events * ticks_per_frame)); + + transmit_at_time = substractTicks(presentation_time, MOTU_TRANSMIT_TRANSFER_DELAY); + presentation_cycle = (unsigned int)(TICKS_TO_CYCLES(presentation_time)); + transmit_at_cycle = (unsigned int)(TICKS_TO_CYCLES(transmit_at_time)); + cycles_until_presentation = diffCycles(presentation_cycle, cycle); + cycles_until_transmit = diffCycles(transmit_at_cycle, cycle); + + if (cycles_until_transmit < 0) + { + if (cycles_until_presentation >= MOTU_MIN_CYCLES_BEFORE_PRESENTATION) + { + m_last_timestamp = presentation_time; + m_tx_dbc += fillDataPacketHeader((quadlet_t *)data, length, m_last_timestamp); + if (m_tx_dbc > 0xff) + m_tx_dbc -= 0x100; + return eCRV_Packet; + } + else + { + return eCRV_XRun; + } + } + else if (cycles_until_transmit <= MOTU_MAX_CYCLES_TO_TRANSMIT_EARLY) + { + m_last_timestamp = presentation_time; + m_tx_dbc += fillDataPacketHeader((quadlet_t *)data, length, m_last_timestamp); + if (m_tx_dbc > 0xff) + m_tx_dbc -= 0x100; + return eCRV_Packet; + } + else + { + return eCRV_EmptyPacket; + } + return eCRV_Invalid; +} + +enum StreamProcessor::eChildReturnValue +MotuTransmitStreamProcessor::generateSilentPacketData ( + unsigned char *data, unsigned int *length ) +{ + // Simply set all audio data to zero since that's what's meant by + // a "silent" packet. Note that m_event_size is in bytes for MOTU. + + quadlet_t *quadlet = (quadlet_t *)data; + quadlet += 2; // skip the header + // Size of a single data frame in quadlets + unsigned dbs = m_event_size / 4; + + // The number of events per packet expected by the MOTU is solely + // dependent on the current sample rate. An 'event' is one sample from + // all channels plus possibly other midi and control data. + signed n_events = getNominalFramesPerPacket(); + + memset(quadlet, 0, n_events*m_event_size); + float ticks_per_frame = m_Parent.getDeviceManager().getStreamProcessorManager().getSyncSource().getTicksPerFrame(); + + // Set up each frames's SPH. + for (int i=0; i < n_events; i++, quadlet += dbs) { + int64_t ts_frame = addTicks(m_last_timestamp, (unsigned int)lrintf(i * ticks_per_frame)); + *quadlet = CondSwapToBus32(fullTicksToSph(ts_frame)); + } + return eCRV_OK; +} + +unsigned int MotuTransmitStreamProcessor::fillDataPacketHeader ( + quadlet_t *data, unsigned int* length, + uint32_t ts ) +{ + quadlet_t *quadlet = (quadlet_t *)data; + // Size of a single data frame in quadlets + unsigned dbs = m_event_size / 4; + + // The number of events per packet expected by the MOTU is solely + // dependent on the current sample rate. An 'event' is one sample from + // all channels plus possibly other midi and control data. + signed n_events = getNominalFramesPerPacket(); + + // construct the packet CIP-like header. Even if this is a data-less + // packet the dbs field is still set as if there were data blocks + // present. For data-less packets the dbc is the same as the previously + // transmitted block. + *quadlet = CondSwapToBus32(0x00000400 | ((m_Parent.get1394Service().getLocalNodeId()&0x3f)<<24) | m_tx_dbc | (dbs<<16)); + quadlet++; + *quadlet = CondSwapToBus32(0x8222ffff); + quadlet++; + return n_events; +} + +unsigned int MotuTransmitStreamProcessor::fillNoDataPacketHeader ( + quadlet_t *data, unsigned int* length ) +{ + quadlet_t *quadlet = (quadlet_t *)data; + // Size of a single data frame in quadlets + unsigned dbs = m_event_size / 4; + // construct the packet CIP-like header. Even if this is a data-less + // packet the dbs field is still set as if there were data blocks + // present. For data-less packets the dbc is the same as the previously + // transmitted block. + *quadlet = CondSwapToBus32(0x00000400 | ((m_Parent.get1394Service().getLocalNodeId()&0x3f)<<24) | m_tx_dbc | (dbs<<16)); + quadlet++; + *quadlet = CondSwapToBus32(0x8222ffff); + quadlet++; + *length = 8; + return 0; +} + +bool MotuTransmitStreamProcessor::prepareChild() +{ + debugOutput ( DEBUG_LEVEL_VERBOSE, "Preparing (%p)...\n", this ); + return true; +} + +/* +* compose the event streams for the packets from the port buffers +*/ +bool MotuTransmitStreamProcessor::processWriteBlock(char *data, + unsigned int nevents, unsigned int offset) { + bool no_problem=true; + unsigned int i; + + // Start with MIDI and control streams all zeroed. Due to the sparce nature + // of these streams it is best to simply fill them in on an as-needs basis. + for (i=0; iisDisabled()) {continue;}; + + Port *port=(*it); + + switch(port->getPortType()) { + + case Port::E_Audio: + if (encodePortToMotuEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { + debugWarning("Could not encode port %s to Motu events",(*it)->getName().c_str()); + no_problem=false; + } + break; + case Port::E_Midi: + if (encodePortToMotuMidiEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { + debugWarning("Could not encode port %s to Midi events",(*it)->getName().c_str()); + no_problem=false; + } + break; + default: // ignore + break; + } + } + return no_problem; +} + +bool +MotuTransmitStreamProcessor::transmitSilenceBlock(char *data, + unsigned int nevents, unsigned int offset) { + // This is the same as the non-silence version, except that is + // doesn't read from the port buffers. + bool no_problem = true; + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) { + Port *port=(*it); + + switch(port->getPortType()) { + + case Port::E_Audio: + if (encodeSilencePortToMotuEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { + debugWarning("Could not encode port %s to MBLA events",(*it)->getName().c_str()); + no_problem = false; + } + break; + case Port::E_Midi: + if (encodeSilencePortToMotuMidiEvents(static_cast(*it), (quadlet_t *)data, offset, nevents)) { + debugWarning("Could not encode port %s to Midi events",(*it)->getName().c_str()); + no_problem = false; + } + break; + default: // ignore + break; + } + } + return no_problem; +} + +int MotuTransmitStreamProcessor::encodePortToMotuEvents(MotuAudioPort *p, quadlet_t *data, + unsigned int offset, unsigned int nevents) { +// Encodes nevents worth of data from the given port into the given buffer. The +// format of the buffer is precisely that which will be sent to the MOTU. +// The basic idea: +// iterate over the ports +// * get port buffer address +// * loop over events +// - pick right sample in event based upon PortInfo +// - convert sample from Port format (E_Int24, E_Float, ..) to MOTU +// native format +// +// We include the ability to start the transfer from the given offset within +// the port (expressed in frames) so the 'efficient' transfer method can be +// utilised. + + unsigned int j=0; + + // Use char here since the target address won't necessarily be + // aligned; use of an unaligned quadlet_t may cause issues on certain + // architectures. Besides, the target (data going directly to the MOTU) + // isn't structured in quadlets anyway; it mainly consists of packed + // 24-bit integers. + unsigned char *target; + target = (unsigned char *)data + p->getPosition(); + + switch(m_StreamProcessorManager.getAudioDataType()) { + default: + case StreamProcessorManager::eADT_Int24: + { + quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); + + assert(nevents + offset <= p->getBufferSize()); + + // Offset is in frames, but each port is only a single + // channel, so the number of frames is the same as the + // number of quadlets to offset (assuming the port buffer + // uses one quadlet per sample, which is the case currently). + buffer+=offset; + + for(j = 0; j < nevents; j += 1) { // Decode nsamples + *target = (*buffer >> 16) & 0xff; + *(target+1) = (*buffer >> 8) & 0xff; + *(target+2) = (*buffer) & 0xff; + + buffer++; + target+=m_event_size; + } + } + break; + case StreamProcessorManager::eADT_Float: + { + const float multiplier = (float)(0x7FFFFF); + float *buffer=(float *)(p->getBufferAddress()); + + assert(nevents + offset <= p->getBufferSize()); + + buffer+=offset; + + for(j = 0; j < nevents; j += 1) { // decode max nsamples + float in = *buffer; +#if MOTU_CLIP_FLOATS + if (unlikely(in > 1.0)) in = 1.0; + if (unlikely(in < -1.0)) in = -1.0; +#endif + unsigned int v = lrintf(in * multiplier); + *target = (v >> 16) & 0xff; + *(target+1) = (v >> 8) & 0xff; + *(target+2) = v & 0xff; + + buffer++; + target+=m_event_size; + } + } + break; + } + + return 0; +} + +int MotuTransmitStreamProcessor::encodeSilencePortToMotuEvents(MotuAudioPort *p, quadlet_t *data, + unsigned int offset, unsigned int nevents) { + unsigned int j=0; + unsigned char *target = (unsigned char *)data + p->getPosition(); + + switch (m_StreamProcessorManager.getAudioDataType()) { + default: + case StreamProcessorManager::eADT_Int24: + case StreamProcessorManager::eADT_Float: + for (j = 0; j < nevents; j++) { + *target = *(target+1) = *(target+2) = 0; + target += m_event_size; + } + break; + } + + return 0; +} + +int MotuTransmitStreamProcessor::encodePortToMotuMidiEvents( + MotuMidiPort *p, quadlet_t *data, + unsigned int offset, unsigned int nevents) { + + unsigned int j; + quadlet_t *src = (quadlet_t *)p->getBufferAddress(); + src += offset; + unsigned char *target = (unsigned char *)data + p->getPosition(); + + // Send a MIDI byte if there is one to send. MOTU MIDI data is sent using + // a 3-byte sequence within a frame starting at the port's position. + // A non-zero MSB indicates there is MIDI data to send. + + for (j=0; jgetPosition(); + + // For now, a "silent" MIDI event contains nothing but zeroes. This + // may have to change if we find this isn't for some reason appropriate. + for (j=0; j. + * + */ + +#include "config.h" +#include "libutil/float_cast.h" + +#include "MotuReceiveStreamProcessor.h" +#include "MotuPort.h" +#include "../StreamProcessorManager.h" +#include "devicemanager.h" + +#include "libieee1394/ieee1394service.h" +#include "libieee1394/IsoHandlerManager.h" +#include "libieee1394/cycletimer.h" + +#include +#include "libutil/ByteSwap.h" +#include + +/* Provide more intuitive access to GCC's branch predition built-ins */ +#define likely(x) __builtin_expect((x),1) +#define unlikely(x) __builtin_expect((x),0) + + +namespace Streaming { + +// A macro to extract specific bits from a native endian quadlet +#define get_bits(_d,_start,_len) (((_d)>>((_start)-(_len)+1)) & ((1<<(_len))-1)) + +// Convert an SPH timestamp as received from the MOTU to a full timestamp in ticks. +static inline uint32_t sphRecvToFullTicks(uint32_t sph, uint32_t ct_now) { + +uint32_t timestamp = CYCLE_TIMER_TO_TICKS(sph & 0x1ffffff); +uint32_t now_cycles = CYCLE_TIMER_GET_CYCLES(ct_now); + +uint32_t ts_sec = CYCLE_TIMER_GET_SECS(ct_now); + // If the cycles have wrapped, correct ts_sec so it represents when timestamp + // was received. The timestamps sent by the MOTU are always 1 or two cycles + // in advance of the cycle timer (reasons unknown at this stage). In addition, + // iso buffering can delay the arrival of packets for quite a number of cycles + // (have seen a delay >12 cycles). + // Every so often we also see sph wrapping ahead of ct_now, so deal with that + // too. + if (unlikely(CYCLE_TIMER_GET_CYCLES(sph) > now_cycles + 1000)) { + if (likely(ts_sec)) + ts_sec--; + else + ts_sec = 127; + } else + if (unlikely(now_cycles > CYCLE_TIMER_GET_CYCLES(sph) + 1000)) { + if (unlikely(ts_sec == 127)) + ts_sec = 0; + else + ts_sec++; + } + return timestamp + ts_sec*TICKS_PER_SECOND; +} + +MotuReceiveStreamProcessor::MotuReceiveStreamProcessor(FFADODevice &parent, unsigned int event_size) + : StreamProcessor(parent, ePT_Receive) + , m_event_size( event_size ) + , mb_head ( 0 ) + , mb_tail ( 0 ) +{ + memset(&m_devctrls, 0, sizeof(m_devctrls)); +} + +unsigned int +MotuReceiveStreamProcessor::getMaxPacketSize() { + int framerate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate(); + return framerate<=48000?616:(framerate<=96000?1032:1160); +} + +unsigned int +MotuReceiveStreamProcessor::getNominalFramesPerPacket() { + int framerate = m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate(); + return framerate<=48000?8:(framerate<=96000?16:32); +} + +bool +MotuReceiveStreamProcessor::prepareChild() { + debugOutput( DEBUG_LEVEL_VERBOSE, "Preparing (%p)...\n", this); + + // prepare the framerate estimate + // FIXME: not needed anymore? + //m_ticks_per_frame = (TICKS_PER_SECOND*1.0) / ((float)m_Parent.getDeviceManager().getStreamProcessorManager().getNominalRate()); + + return true; +} + + +/** + * Processes packet header to extract timestamps and check if the packet is valid + * @param data + * @param length + * @param channel + * @param tag + * @param sy + * @param cycle + * @return + */ +enum StreamProcessor::eChildReturnValue +MotuReceiveStreamProcessor::processPacketHeader(unsigned char *data, unsigned int length, + unsigned char tag, unsigned char sy, + uint32_t pkt_ctr) +{ + if (length > 8) { + // The iso data blocks from the MOTUs comprise a CIP-like + // header followed by a number of events (8 for 1x rates, 16 + // for 2x rates, 32 for 4x rates). + quadlet_t *quadlet = (quadlet_t *)data; + unsigned int dbs = get_bits(CondSwapFromBus32(quadlet[0]), 23, 8); // Size of one event in terms of fdf_size + unsigned int fdf_size = get_bits(CondSwapFromBus32(quadlet[1]), 23, 8) == 0x22 ? 32:0; // Event unit size in bits + + // Don't even attempt to process a packet if it isn't what + // we expect from a MOTU. Yes, an FDF value of 32 bears + // little relationship to the actual data (24 bit integer) + // sent by the MOTU - it's one of those areas where MOTU + // have taken a curious detour around the standards. + if (tag!=1 || fdf_size!=32) { + return eCRV_Invalid; + } + + // put this after the check because event_length can become 0 on invalid packets + unsigned int event_length = (fdf_size * dbs) / 8; // Event size in bytes + unsigned int n_events = (length-8) / event_length; + + // Acquire the timestamp of the last frame in the packet just + // received. Since every frame from the MOTU has its own timestamp + // we can just pick it straight from the packet. + uint32_t last_sph = CondSwapFromBus32(*(quadlet_t *)(data+8+(n_events-1)*event_length)); + m_last_timestamp = sphRecvToFullTicks(last_sph, m_Parent.get1394Service().getCycleTimer()); + + return eCRV_OK; + } else { + return eCRV_Invalid; + } +} + +/** + * extract the data from the packet + * @pre the IEC61883 packet is valid according to isValidPacket + * @param data + * @param length + * @param channel + * @param tag + * @param sy + * @param pkt_ctr + * @return + */ +enum StreamProcessor::eChildReturnValue +MotuReceiveStreamProcessor::processPacketData(unsigned char *data, unsigned int length) { + quadlet_t* quadlet = (quadlet_t*) data; + + unsigned int dbs = get_bits(CondSwapFromBus32(quadlet[0]), 23, 8); // Size of one event in terms of fdf_size + unsigned int fdf_size = get_bits(CondSwapFromBus32(quadlet[1]), 23, 8) == 0x22 ? 32:0; // Event unit size in bits + // this is only called for packets that return eCRV_OK on processPacketHeader + // so event_length won't become 0 + unsigned int event_length = (fdf_size * dbs) / 8; // Event size in bytes + unsigned int n_events = (length-8) / event_length; + + // we have to keep in mind that there are also + // some packets buffered by the ISO layer, + // at most x=m_handler->getWakeupInterval() + // these contain at most x*syt_interval + // frames, meaning that we might receive + // this packet x*syt_interval*ticks_per_frame + // later than expected (the real receive time) + #ifdef DEBUG + if(isRunning()) { + debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"STMP: %lluticks | tpf=%f\n", + m_last_timestamp, getTicksPerFrame()); + } + #endif + + if(m_data_buffer->writeFrames(n_events, (char *)(data+8), m_last_timestamp)) { + return eCRV_OK; + } else { + return eCRV_XRun; + } +} + +/*********************************************** + * Encoding/Decoding API * + ***********************************************/ +/** + * \brief write received events to the port ringbuffers. + */ +bool MotuReceiveStreamProcessor::processReadBlock(char *data, + unsigned int nevents, unsigned int offset) +{ + bool no_problem=true; + + /* Scan incoming block for device control events */ + decodeMotuCtrlEvents(data, nevents); + + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) { + if((*it)->isDisabled()) {continue;}; + + Port *port=(*it); + + switch(port->getPortType()) { + + case Port::E_Audio: + if(decodeMotuEventsToPort(static_cast(*it), (quadlet_t *)data, offset, nevents)) { + debugWarning("Could not decode packet data to port %s",(*it)->getName().c_str()); + no_problem=false; + } + break; + case Port::E_Midi: + if(decodeMotuMidiEventsToPort(static_cast(*it), (quadlet_t *)data, offset, nevents)) { + debugWarning("Could not decode packet midi data to port %s",(*it)->getName().c_str()); + no_problem=false; + } + break; + + default: // ignore + break; + } + } + return no_problem; +} + +signed int MotuReceiveStreamProcessor::decodeMotuEventsToPort(MotuAudioPort *p, + quadlet_t *data, unsigned int offset, unsigned int nevents) +{ + unsigned int j=0; + + // Use char here since a port's source address won't necessarily be + // aligned; use of an unaligned quadlet_t may cause issues on + // certain architectures. Besides, the source (data coming directly + // from the MOTU) isn't structured in quadlets anyway; it mainly + // consists of packed 24-bit integers. + + unsigned char *src_data; + src_data = (unsigned char *)data + p->getPosition(); + + switch(m_StreamProcessorManager.getAudioDataType()) { + default: + case StreamProcessorManager::eADT_Int24: + { + quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); + + assert(nevents + offset <= p->getBufferSize()); + + // Offset is in frames, but each port is only a single + // channel, so the number of frames is the same as the + // number of quadlets to offset (assuming the port buffer + // uses one quadlet per sample, which is the case currently). + buffer+=offset; + + for(j = 0; j < nevents; j += 1) { // Decode nsamples + *buffer = (*src_data<<16)+(*(src_data+1)<<8)+*(src_data+2); + // Sign-extend highest bit of 24-bit int. + // FIXME: this isn't strictly needed since E_Int24 is a 24-bit, + // but doing so shouldn't break anything and makes the data + // easier to deal with during debugging. + if (*src_data & 0x80) + *buffer |= 0xff000000; + + buffer++; + src_data+=m_event_size; + } + } + break; + case StreamProcessorManager::eADT_Float: + { + const float multiplier = 1.0f / (float)(0x7FFFFF); + float *buffer=(float *)(p->getBufferAddress()); + + assert(nevents + offset <= p->getBufferSize()); + + buffer+=offset; + + for(j = 0; j < nevents; j += 1) { // decode max nsamples + + signed int v = (*src_data<<16)+(*(src_data+1)<<8)+*(src_data+2); + /* Sign-extend highest bit of incoming 24-bit integer */ + if (*src_data & 0x80) + v |= 0xff000000; + *buffer = v * multiplier; + buffer++; + src_data+=m_event_size; + } + } + break; + } + + return 0; +} + +int +MotuReceiveStreamProcessor::decodeMotuMidiEventsToPort( + MotuMidiPort *p, quadlet_t *data, + unsigned int offset, unsigned int nevents) +{ + unsigned int j = 0; + unsigned char *src = NULL; + + quadlet_t *buffer = (quadlet_t *)(p->getBufferAddress()); + assert(nevents + offset <= p->getBufferSize()); + buffer += offset; + + // Zero the buffer + memset(buffer, 0, nevents*sizeof(*buffer)); + + // Get MIDI bytes if present in any frames within the packet. MOTU MIDI + // data is sent as part of a 3-byte sequence starting at the port's + // position. Some MOTUs (eg: the 828MkII) send more than one MIDI byte + // in some packets. Since the FFADO MIDI layer requires a MIDI byte in + // only every 8th buffer position we allow for this by buffering the + // incoming data. The buffer is small since it only has to cover for + // short-term excursions in the data rate. Since the MIDI data + // originates on a physical MIDI bus the overall data rate is limited by + // the baud rate of that bus (31250), which is no more than one byte in + // 8 even for 1x sample rates. + src = (unsigned char *)data + p->getPosition(); + // We assume that the buffer has been set up in such a way that the first + // element is correctly aligned for FFADOs MIDI layer. The requirement + // is that actual MIDI bytes must be aligned to multiples of 8 samples. + + while (j < nevents) { + /* Most events don't have MIDI data bytes */ + if (unlikely((*src & MOTU_KEY_MASK_MIDI) == MOTU_KEY_MASK_MIDI)) { + // A MIDI byte is in *(src+2). Bit 24 is used to flag MIDI data + // as present once the data makes it to the output buffer. + midibuffer[mb_head++] = 0x01000000 | *(src+2); + mb_head &= RX_MIDIBUFFER_SIZE-1; + if (unlikely(mb_head == mb_tail)) { + debugWarning("MOTU rx MIDI buffer overflow\n"); + /* Dump oldest byte. This overflow can only happen if the + * rate coming in from the hardware MIDI port grossly + * exceeds the official MIDI baud rate of 31250 bps, so it + * should never occur in practice. + */ + mb_tail = (mb_tail + 1) & (RX_MIDIBUFFER_SIZE-1); + } + } + /* Write to the buffer if we're at an 8-sample boundary */ + if (unlikely(!(j & 0x07))) { + if (mb_head != mb_tail) { + *buffer = midibuffer[mb_tail++]; + mb_tail &= RX_MIDIBUFFER_SIZE-1; + } + buffer += 8; + } + j++; + src += m_event_size; + } + + return 0; +} + +int +MotuReceiveStreamProcessor::decodeMotuCtrlEvents( + char *data, unsigned int nevents) +{ + unsigned int j = 0; + unsigned char *src = NULL; + unsigned char *arg = NULL; + + // Get control bytes if present in any frames within the packet. The + // device control messages start at (zero-based) byte 0x04 in the data + // stream. + src = (unsigned char *)data + 0x04; + arg = src+1; + while (j < nevents) { + unsigned int control_key = *src & ~MOTU_KEY_MASK_MIDI; + + if (m_devctrls.status == MOTU_DEVCTRL_INVALID) { + // Start syncing on reception of the sequence sync key which indicates + // mix bus 1 values are pending. Acquisition will start when we see the + // first channel gain key after this. + if (control_key==MOTU_KEY_SEQ_SYNC && *arg==MOTU_KEY_SEQ_SYNC_MIXBUS1) { + debugOutput(DEBUG_LEVEL_VERBOSE, "syncing device control status stream\n"); + m_devctrls.status = MOTU_DEVCTRL_SYNCING; + } + } else + if (m_devctrls.status == MOTU_DEVCTRL_SYNCING) { + // Start acquiring when we see a channel gain key for mixbus 1. + if (control_key == MOTU_KEY_SEQ_SYNC) { + // Keep mixbus index updated since we don't execute the main parser until + // we move to the initialising state. Since we don't dereference this until + // we know it's equal to 0 there's no need for bounds checking here. + m_devctrls.mixbus_index = *arg; + } else + if (control_key==MOTU_KEY_CHANNEL_GAIN && m_devctrls.mixbus_index==0) { + debugOutput(DEBUG_LEVEL_VERBOSE, "initialising device control status\n"); + m_devctrls.status = MOTU_DEVCTRL_INIT; + } + } else + if (m_devctrls.status == MOTU_DEVCTRL_INIT) { + // Consider ourselves fully initialised when a control sequence sync key + // arrives which takes things back to mixbus 1. + if (control_key==MOTU_KEY_SEQ_SYNC && *arg==MOTU_KEY_SEQ_SYNC_MIXBUS1 && m_devctrls.mixbus_index>0) { + debugOutput(DEBUG_LEVEL_VERBOSE, "device control status valid: n_mixbuses=%d, n_channels=%d\n", + m_devctrls.n_mixbuses, m_devctrls.n_channels); + m_devctrls.status = MOTU_DEVCTRL_VALID; + } + } + + if (m_devctrls.status==MOTU_DEVCTRL_INIT || m_devctrls.status==MOTU_DEVCTRL_VALID) { + unsigned int i; + switch (control_key) { + case MOTU_KEY_SEQ_SYNC: + if (m_devctrls.mixbus_index < MOTUFW_MAX_MIXBUSES) { + if (m_devctrls.n_channels==0 && m_devctrls.mixbus[m_devctrls.mixbus_index].channel_gain_index!=0) { + m_devctrls.n_channels = m_devctrls.mixbus[m_devctrls.mixbus_index].channel_gain_index; + } + } + /* Mix bus to configure next is in bits 5-7 of the argument */ + m_devctrls.mixbus_index = (*arg >> 5); + if (m_devctrls.mixbus_index >= MOTUFW_MAX_MIXBUSES) { + debugWarning("MOTU cuemix value parser error: mix bus index %d exceeded maximum %d\n", + m_devctrls.mixbus_index, MOTUFW_MAX_MIXBUSES); + } else { + if (m_devctrls.n_mixbuses < m_devctrls.mixbus_index+1) { + m_devctrls.n_mixbuses = m_devctrls.mixbus_index+1; + } + m_devctrls.mixbus[m_devctrls.mixbus_index].channel_gain_index = + m_devctrls.mixbus[m_devctrls.mixbus_index].channel_pan_index = + m_devctrls.mixbus[m_devctrls.mixbus_index].channel_control_index = 0; + } + break; + case MOTU_KEY_CHANNEL_GAIN: + i = m_devctrls.mixbus[m_devctrls.mixbus_index].channel_gain_index++; + if (m_devctrls.mixbus_index= MOTUFW_MAX_MIXBUS_CHANNELS) { + debugWarning("MOTU cuemix value parser error: channel gain index %d exceeded maximum %d\n", + i, MOTUFW_MAX_MIXBUS_CHANNELS); + } + break; + case MOTU_KEY_CHANNEL_PAN: + i = m_devctrls.mixbus[m_devctrls.mixbus_index].channel_pan_index++; + if (m_devctrls.mixbus_index= MOTUFW_MAX_MIXBUS_CHANNELS) { + debugWarning("MOTU cuemix value parser error: channel pan index %d exceeded maximum %d\n", + i, MOTUFW_MAX_MIXBUS_CHANNELS); + } + break; + case MOTU_KEY_CHANNEL_CTRL: + i = m_devctrls.mixbus[m_devctrls.mixbus_index].channel_control_index++; + if (m_devctrls.mixbus_index= MOTUFW_MAX_MIXBUS_CHANNELS) { + debugWarning("MOTU cuemix value parser error: channel control index %d exceeded maximum %d\n", + i, MOTUFW_MAX_MIXBUS_CHANNELS); + } + break; + case MOTU_KEY_MIXBUS_GAIN: + if (m_devctrls.mixbus_index < MOTUFW_MAX_MIXBUSES) { + m_devctrls.mixbus[m_devctrls.mixbus_index].bus_gain = *arg; + } + break; + case MOTU_KEY_MIXBUS_DEST: + if (m_devctrls.mixbus_index < MOTUFW_MAX_MIXBUSES) { + m_devctrls.mixbus[m_devctrls.mixbus_index].bus_dest = *arg; + } + break; + case MOTU_KEY_MAINOUT_VOL: + m_devctrls.main_out_volume = *arg; + break; + case MOTU_KEY_PHONES_VOL: + m_devctrls.phones_volume = *arg; + break; + case MOTU_KEY_PHONES_DEST: + m_devctrls.phones_assign = *arg; + break; + case MOTU_KEY_INPUT_6dB_BOOST: + m_devctrls.input_6dB_boost = *arg; + break; + case MOTU_KEY_INPUT_REF_LEVEL: + m_devctrls.input_ref_level = *arg; + break; + case MOTU_KEY_MIDI: + // MIDI is dealt with elsewhere, so just pass it over + break; + default: + // Ignore any unknown keys or those we don't care about, at + // least for now. + break; + } + } + j++; + src += m_event_size; + arg += m_event_size; + } + + return 0; +} + +} // end of namespace Streaming Index: /branches/libffado-2.0/src/libstreaming/motu/MotuPortInfo.cpp =================================================================== --- /branches/libffado-2.0/src/libstreaming/motu/MotuPortInfo.cpp (revision 864) +++ /branches/libffado-2.0/src/libstreaming/motu/MotuPortInfo.cpp (revision 864) @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2008 by Jonathan Woithe + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "MotuPortInfo.h" + +namespace Streaming { + + +} // end of namespace Streaming Index: /branches/libffado-2.0/src/libstreaming/motu/MotuTransmitStreamProcessor.h =================================================================== --- /branches/libffado-2.0/src/libstreaming/motu/MotuTransmitStreamProcessor.h (revision 1034) +++ /branches/libffado-2.0/src/libstreaming/motu/MotuTransmitStreamProcessor.h (revision 1034) @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2005-2008 by Jonathan Woithe + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_MOTUTRANSMITSTREAMPROCESSOR__ +#define __FFADO_MOTUTRANSMITSTREAMPROCESSOR__ + +/** + * This class implements MOTU based streaming + */ + +#include "debugmodule/debugmodule.h" + +#include "../generic/StreamProcessor.h" +#include "../util/cip.h" + +namespace Streaming { + +class Port; +class MotuAudioPort; +class MotuMidiPort; +/*! +\brief The Base Class for an MOTU transmit stream processor + + This class implements a TransmitStreamProcessor that multiplexes Ports + into MOTU streams. + +*/ +class MotuTransmitStreamProcessor + : public StreamProcessor +{ + +public: + /** + * Create a MOTU transmit StreamProcessor + */ + MotuTransmitStreamProcessor(FFADODevice &parent, unsigned int event_size); + virtual ~MotuTransmitStreamProcessor() {}; + + enum eChildReturnValue generatePacketHeader(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr); + enum eChildReturnValue generatePacketData(unsigned char *data, unsigned int *length); + enum eChildReturnValue generateEmptyPacketHeader(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr); + enum eChildReturnValue generateEmptyPacketData(unsigned char *data, unsigned int *length); + enum eChildReturnValue generateSilentPacketHeader(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr); + enum eChildReturnValue generateSilentPacketData(unsigned char *data, unsigned int *length); + virtual bool prepareChild(); + +public: + virtual unsigned int getEventSize() + {return m_event_size;}; + virtual unsigned int getMaxPacketSize(); + virtual unsigned int getEventsPerFrame() + { return 1; }; // FIXME: check + virtual unsigned int getNominalFramesPerPacket(); + +protected: + bool processWriteBlock(char *data, unsigned int nevents, unsigned int offset); + bool transmitSilenceBlock(char *data, unsigned int nevents, unsigned int offset); + +private: + unsigned int fillNoDataPacketHeader(quadlet_t *data, unsigned int* length); + unsigned int fillDataPacketHeader(quadlet_t *data, unsigned int* length, uint32_t ts); + + int transmitBlock(char *data, unsigned int nevents, + unsigned int offset); + + bool encodePacketPorts(quadlet_t *data, unsigned int nevents, + unsigned int dbc); + + int encodePortToMotuEvents(MotuAudioPort *, quadlet_t *data, + unsigned int offset, unsigned int nevents); + int encodeSilencePortToMotuEvents(MotuAudioPort *, quadlet_t *data, + unsigned int offset, unsigned int nevents); + + int encodePortToMotuMidiEvents( + MotuMidiPort *p, quadlet_t *data, + unsigned int offset, unsigned int nevents); + int encodeSilencePortToMotuMidiEvents( + MotuMidiPort *p, quadlet_t *data, + unsigned int offset, unsigned int nevents); + + /* + * An iso packet mostly consists of multiple events. m_event_size + * is the size of a single 'event' in bytes. + */ + unsigned int m_event_size; + + // Keep track of transmission data block count + unsigned int m_tx_dbc; + + // A simple circular buffer for outgoing MIDI data to allow + // a rate control to be implemented on the data to suit the MOTU + // devices. Note that this buffer's size is forced to be a power + // of 2 to allow for buffer manipulation optimisations. + #define MIDIBUFFER_SIZE_EXP 10 + #define MIDIBUFFER_SIZE (1<. + * + */ + +#ifndef __FFADO_MOTURECEIVESTREAMPROCESSOR__ +#define __FFADO_MOTURECEIVESTREAMPROCESSOR__ + +/** + * This class implements MOTU streaming + */ + +#include "debugmodule/debugmodule.h" + +#include "../generic/StreamProcessor.h" +#include "../util/cip.h" + +namespace Streaming { + +#define MOTUFW_MAX_MIXBUSES 4 +#define MOTUFW_MAX_MIXBUS_CHANNELS 20 + +#define MOTU_CHANNEL_NORMAL 0x00 +#define MOTU_CHANNEL_MUTE 0x01 +#define MOTU_CHANNEL_SOLO 0x02 +#define MOTU_CHANNEL_PAIRED 0x08 + +#define MOTU_CHANNEL_PAN_LEFT 0x00 +#define MOTU_CHANNEL_PAN_RIGHT 0x80 +#define MOTU_CHANNEL_PAN_CENTRE 0x40 + +#define MOTU_DEST_DISABLED 0x00 +#define MOTU_DEST_HEADPHONE 0x01 +#define MOTU_DEST_ANALOG1_2 0x02 +#define MOTU_DEST_ANALOG3_4 0x03 +#define MOTU_DEST_ANALOG5_6 0x04 +#define MOTU_DEST_ANALOG7_8 0x05 +#define MOTU_DEST_AESEBU 0x06 +#define MOTU_DEST_SPDIF 0x07 +#define MOTU_DEST_ADAT1_1_2 0x08 +#define MOTU_DEST_ADAT1_3_4 0x09 +#define MOTU_DEST_ADAT1_5_6 0x0a +#define MOTU_DEST_ADAT1_7_8 0x0b +#define MOTU_DEST_MUTE 0x10 + +enum EMotuDevCtrlStatus { + MOTU_DEVCTRL_INVALID = 0x00, + MOTU_DEVCTRL_SYNCING = 0x01, + MOTU_DEVCTRL_INIT = 0x02, + MOTU_DEVCTRL_VALID = 0x03, +}; + +struct MotuDevControls { + unsigned int status; + unsigned int input_6dB_boost; + unsigned int input_ref_level; + unsigned int input_20dB_pad; + unsigned int input_gaintrim[MOTUFW_MAX_MIXBUS_CHANNELS]; + unsigned char input_gaintrim_index; + struct MixBus { + unsigned char channel_gain[MOTUFW_MAX_MIXBUS_CHANNELS]; + unsigned char channel_gain_index; + unsigned char channel_pan[MOTUFW_MAX_MIXBUS_CHANNELS]; + unsigned char channel_pan_index; + unsigned char channel_control[MOTUFW_MAX_MIXBUS_CHANNELS]; + unsigned char channel_control_index; + unsigned char bus_gain; + unsigned char bus_dest; + } mixbus[MOTUFW_MAX_MIXBUSES]; + unsigned char mixbus_index; + unsigned char main_out_volume; + unsigned char phones_volume; + unsigned char phones_assign; + unsigned char n_mixbuses; + unsigned char n_channels; +}; + +enum EMotuCtrlKeys { + MOTU_KEY_MIDI = 0x01, + MOTU_KEY_SEQ_SYNC = 0x0c, + MOTU_KEY_CHANNEL_GAIN = 0x14, + MOTU_KEY_CHANNEL_PAN = 0x1c, + MOTU_KEY_CHANNEL_CTRL = 0x24, + MOTU_KEY_MIXBUS_GAIN = 0x2c, + MOTU_KEY_MIXBUS_DEST = 0x34, + MOTU_KEY_MAINOUT_VOL = 0x3c, + MOTU_KEY_PHONES_VOL = 0x44, + MOTU_KEY_PHONES_DEST = 0x4c, + MOTU_KEY_INPUT_6dB_BOOST = 0x6c, + MOTU_KEY_INPUT_REF_LEVEL = 0x74, + MOTU_KEY_MASK_MIDI = 0x01, +}; + +enum EMotuSeqSyncMixbuses { + MOTU_KEY_SEQ_SYNC_MIXBUS1 = 0x00, + MOTU_KEY_SEQ_SYNC_MIXBUS2 = 0x20, + MOTU_KEY_SEQ_SYNC_MIXBUS3 = 0x40, + MOTU_KEY_SEQ_SYNC_MIXBUS4 = 0x60, +}; + +class MotuAudioPort; +class MotuMidiPort; +/*! + * \brief The Base Class for a MOTU receive stream processor + * + * This class implements the outgoing stream processing for + * motu devices + * + */ +class MotuReceiveStreamProcessor + : public StreamProcessor +{ + +public: + /** + * Create a MOTU receive StreamProcessor + * @param port 1394 port + * @param dimension number of substreams in the ISO stream + * (midi-muxed is only one stream) + */ + MotuReceiveStreamProcessor(FFADODevice &parent, unsigned int event_size); + virtual ~MotuReceiveStreamProcessor() {}; + + enum eChildReturnValue processPacketHeader(unsigned char *data, unsigned int length, + unsigned char tag, unsigned char sy, + uint32_t pkt_ctr); + enum eChildReturnValue processPacketData(unsigned char *data, unsigned int length); + + virtual bool prepareChild(); + +public: + virtual unsigned int getEventSize() + {return m_event_size;}; + virtual unsigned int getMaxPacketSize(); + virtual unsigned int getEventsPerFrame() + { return 1; }; // FIXME: check + virtual unsigned int getNominalFramesPerPacket(); + +protected: + bool processReadBlock(char *data, unsigned int nevents, unsigned int offset); + +private: + bool decodePacketPorts(quadlet_t *data, unsigned int nevents, unsigned int dbc); + + int decodeMotuEventsToPort(MotuAudioPort *, quadlet_t *data, unsigned int offset, unsigned int nevents); + int decodeMotuMidiEventsToPort(MotuMidiPort *, quadlet_t *data, unsigned int offset, unsigned int nevents); + int decodeMotuCtrlEvents(char *data, unsigned int nevents); + + /* + * An iso packet mostly consists of multiple events. m_event_size + * is the size of a single 'event' in bytes. + */ + unsigned int m_event_size; + + struct MotuDevControls m_devctrls; + + /* A small MIDI buffer to cover for the case where we need to span a + * period. This can only occur if more than one MIDI byte is sent per + * packet, but this has been observed with some MOTUs (eg: 828MkII). + * Since the long-term average data rate must be close to the MIDI spec + * since it's coming from a physical MIDI port this buffer doesn't have + * to be particularly large. The size is a power of 2 for optimisation + * reasons. + */ + #define RX_MIDIBUFFER_SIZE_EXP 6 + #define RX_MIDIBUFFER_SIZE (1<. + * + */ + +#include "MotuPort.h" +#include + +namespace Streaming { + +} // end of namespace Streaming Index: /branches/libffado-2.0/src/libstreaming/motu/MotuPortInfo.h =================================================================== --- /branches/libffado-2.0/src/libstreaming/motu/MotuPortInfo.h (revision 864) +++ /branches/libffado-2.0/src/libstreaming/motu/MotuPortInfo.h (revision 864) @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005-2008 by Jonathan Woithe + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_MOTUPORTINFO__ +#define __FFADO_MOTUPORTINFO__ + +#include "debugmodule/debugmodule.h" +#include + +namespace Streaming { +/*! +\brief Class containing the stream information for a Motu channel + + Contains the information that enables the decoding routine to find + this port's data in the ISO events + +*/ +class MotuPortInfo { + +public: + /** + * Sometimes a channel can have multiple formats, depending on the + * device configuration (e.g. an SPDIF port could be plain audio in 24bit integer + * or AC3 passthrough in IEC compliant frames.) + * + * This kind of enum allows to discriminate these formats when decoding + * If all channels always have the same format, you won't be needing this + */ +// enum E_Formats { +// E_MBLA, // Multibit linear audio +// E_Midi, // MIDI +// }; + + /** + * Initialize Motu portinfo + * should not be called directly, is inherited by motu ports + * + * the position parameter is an example + * the name parameter is mandatory + * + * @param position Start position of port's data in iso event + * @param format Format of data in iso event + * @param size Size in bits of port's data in iso event + * @return + */ + MotuPortInfo( int position, int size) + : m_position(position), m_size(size) + {}; + virtual ~MotuPortInfo() {}; + + + int getPosition() {return m_position;}; + int getSize() {return m_size;}; + +protected: + + int m_position; + int m_size; + +}; + +} // end of namespace Streaming + +#endif /* __FFADO_MOTUPORTINFO__ */ Index: /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpPort.h =================================================================== --- /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpPort.h (revision 864) +++ /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpPort.h (revision 864) @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_AMDTPPORT__ +#define __FFADO_AMDTPPORT__ + +/** + * This file implements the AMDTP ports as used in the BeBoB's + */ + +#include "debugmodule/debugmodule.h" +#include "../generic/Port.h" +#include "AmdtpPortInfo.h" + +namespace Streaming { + +/*! +\brief The Base Class for an AMDTP Audio Port + + The AMDTP/AM824/IEC61883-6 port that represents audio. + +*/ +class AmdtpAudioPort + : public AudioPort, public AmdtpPortInfo +{ + +public: + + AmdtpAudioPort(PortManager &m, + std::string name, + enum E_Direction direction, + int position, + int location, + enum E_Formats format) + : AudioPort(m, name, direction), + AmdtpPortInfo(position, location, format) + {}; + + virtual ~AmdtpAudioPort() {}; +}; + +/*! +\brief The Base Class for an AMDTP Midi Port + + The AMDTP/AM824/IEC61883-6 port that represents midi. + +*/ +class AmdtpMidiPort + : public MidiPort, public AmdtpPortInfo +{ + +public: + + AmdtpMidiPort(PortManager &m, + std::string name, + enum E_Direction direction, + int position, + int location, + enum E_Formats format) + : MidiPort(m, name, direction), + AmdtpPortInfo(position, location, format) + {}; + + virtual ~AmdtpMidiPort() {}; +}; + +} // end of namespace Streaming + +#endif /* __FFADO_AMDTPPORT__ */ Index: /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpTransmitStreamProcessor.cpp =================================================================== --- /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpTransmitStreamProcessor.cpp (revision 1149) +++ /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpTransmitStreamProcessor.cpp (revision 1149) @@ -0,0 +1,1146 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "config.h" +#include "AmdtpTransmitStreamProcessor.h" +#include "AmdtpPort.h" +#include "../StreamProcessorManager.h" +#include "devicemanager.h" + +#include "libutil/Time.h" +#include "libutil/float_cast.h" + +#include "libieee1394/ieee1394service.h" +#include "libieee1394/IsoHandlerManager.h" +#include "libieee1394/cycletimer.h" + +#include "libutil/ByteSwap.h" +#include + +#define AMDTP_FLOAT_MULTIPLIER 2147483392.0 + +namespace Streaming +{ + +/* transmit */ +AmdtpTransmitStreamProcessor::AmdtpTransmitStreamProcessor(FFADODevice &parent, int dimension) + : StreamProcessor(parent, ePT_Transmit) + , m_dimension( dimension ) + , m_dbc( 0 ) +#if AMDTP_ALLOW_PAYLOAD_IN_NODATA_XMIT + , m_send_nodata_payload ( AMDTP_SEND_PAYLOAD_IN_NODATA_XMIT_BY_DEFAULT ) +#endif + , m_nb_audio_ports( 0 ) + , m_nb_midi_ports( 0 ) +{} + +enum StreamProcessor::eChildReturnValue +AmdtpTransmitStreamProcessor::generatePacketHeader ( + unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr ) +{ + __builtin_prefetch(data, 1, 0); // prefetch events for write, no temporal locality + struct iec61883_packet *packet = (struct iec61883_packet *)data; + /* Our node ID can change after a bus reset, so it is best to fetch + * our node ID for each packet. */ + packet->sid = m_local_node_id; + + packet->dbs = m_dimension; + packet->fn = 0; + packet->qpc = 0; + packet->sph = 0; + packet->reserved = 0; + packet->dbc = m_dbc; + packet->eoh1 = 2; + packet->fmt = IEC61883_FMT_AMDTP; + + *tag = IEC61883_TAG_WITH_CIP; + *sy = 0; + + signed int fc; + uint64_t presentation_time; + unsigned int presentation_cycle; + int cycles_until_presentation; + + uint64_t transmit_at_time; + unsigned int transmit_at_cycle; + int cycles_until_transmit; + + debugOutputExtreme( DEBUG_LEVEL_ULTRA_VERBOSE, + "Try for cycle %d\n", CYCLE_TIMER_GET_CYCLES(pkt_ctr) ); + // check whether the packet buffer has packets for us to send. + // the base timestamp is the one of the next sample in the buffer + ffado_timestamp_t ts_head_tmp; + m_data_buffer->getBufferHeadTimestamp( &ts_head_tmp, &fc ); // thread safe + + // the timestamp gives us the time at which we want the sample block + // to be output by the device + presentation_time = ( uint64_t ) ts_head_tmp; + + // now we calculate the time when we have to transmit the sample block + transmit_at_time = substractTicks( presentation_time, AMDTP_TRANSMIT_TRANSFER_DELAY ); + + // calculate the cycle this block should be presented in + // (this is just a virtual calculation since at that time it should + // already be in the device's buffer) + presentation_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( presentation_time ) ); + + // calculate the cycle this block should be transmitted in + transmit_at_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( transmit_at_time ) ); + + // we can check whether this cycle is within the 'window' we have + // to send this packet. + // first calculate the number of cycles left before presentation time + cycles_until_presentation = diffCycles ( presentation_cycle, CYCLE_TIMER_GET_CYCLES(pkt_ctr) ); + + // we can check whether this cycle is within the 'window' we have + // to send this packet. + // first calculate the number of cycles left before presentation time + cycles_until_transmit = diffCycles ( transmit_at_cycle, CYCLE_TIMER_GET_CYCLES(pkt_ctr) ); + + // two different options: + // 1) there are not enough frames for one packet + // => determine wether this is a problem, since we might still + // have some time to send it + // 2) there are enough packets + // => determine whether we have to send them in this packet + if ( fc < ( signed int ) m_syt_interval ) + { + // not enough frames in the buffer, + + // we can still postpone the queueing of the packets + // if we are far enough ahead of the presentation time + if ( cycles_until_presentation <= AMDTP_MIN_CYCLES_BEFORE_PRESENTATION ) + { + debugOutput( DEBUG_LEVEL_NORMAL, + "Insufficient frames (P): N=%02d, CY=%04u, TC=%04u, CUT=%04d\n", + fc, CYCLE_TIMER_GET_CYCLES(pkt_ctr), + transmit_at_cycle, cycles_until_transmit ); + // we are too late + return eCRV_XRun; + } + else + { + #if DEBUG_EXTREME + unsigned int now_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( m_1394service.getCycleTimerTicks() ) ); + + debugOutputExtreme(DEBUG_LEVEL_VERBOSE, + "Insufficient frames (NP): N=%02d, CY=%04u, TC=%04u, CUT=%04d, NOW=%04d\n", + fc, CYCLE_TIMER_GET_CYCLES(pkt_ctr), + transmit_at_cycle, cycles_until_transmit, now_cycle ); + #endif + + // there is still time left to send the packet + // we want the system to give this packet another go at a later time instant + return eCRV_Again; // note that the raw1394 again system doesn't work as expected + + // we could wait here for a certain time before trying again. However, this + // is not going to work since we then block the iterator thread, hence also + // the receiving code, meaning that we are not processing received packets, + // and hence there is no progression in the number of frames available. + + // for example: + // SleepRelativeUsec(125); // one cycle + // goto try_block_of_frames; + + // or more advanced, calculate how many cycles we are ahead of 'now' and + // base the sleep on that. + + // note that this requires that there is one thread for each IsoHandler, + // otherwise we're in the deadlock described above. + } + } + else + { + // there are enough frames, so check the time they are intended for + // all frames have a certain 'time window' in which they can be sent + // this corresponds to the range of the timestamp mechanism: + // we can send a packet 15 cycles in advance of the 'presentation time' + // in theory we can send the packet up till one cycle before the presentation time, + // however this is not very smart. + + // There are 3 options: + // 1) the frame block is too early + // => send an empty packet + // 2) the frame block is within the window + // => send it + // 3) the frame block is too late + // => discard (and raise xrun?) + // get next block of frames and repeat + + if(cycles_until_transmit < 0) + { + // we are too late + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, + "Too late: CY=%04u, TC=%04u, CUT=%04d, TSP=%011llu (%04u)\n", + CYCLE_TIMER_GET_CYCLES(pkt_ctr), + transmit_at_cycle, cycles_until_transmit, + presentation_time, (unsigned int)TICKS_TO_CYCLES(presentation_time) ); + //debugShowBackLogLines(200); + // however, if we can send this sufficiently before the presentation + // time, it could be harmless. + // NOTE: dangerous since the device has no way of reporting that it didn't get + // this packet on time. + if(cycles_until_presentation >= AMDTP_MIN_CYCLES_BEFORE_PRESENTATION) + { + // we are not that late and can still try to transmit the packet + m_dbc += fillDataPacketHeader(packet, length, presentation_time); + m_last_timestamp = presentation_time; + return (fc < (signed)(2*m_syt_interval) ? eCRV_Defer : eCRV_Packet); + } + else // definitely too late + { + return eCRV_XRun; + } + } + else if(cycles_until_transmit <= AMDTP_MAX_CYCLES_TO_TRANSMIT_EARLY) + { + // it's time send the packet + m_dbc += fillDataPacketHeader(packet, length, presentation_time); + m_last_timestamp = presentation_time; + + // for timestamp tracing + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "XMIT PKT: TSP= %011llu (%04u) (%04u) (%04u)\n", + presentation_time, + (unsigned int)CYCLE_TIMER_GET_CYCLES(pkt_ctr), + presentation_cycle, transmit_at_cycle); + + return (fc < (signed)(m_syt_interval) ? eCRV_Defer : eCRV_Packet); + } + else + { + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "Too early: CY=%04u, TC=%04u, CUT=%04d, TST=%011llu (%04u), TSP=%011llu (%04u)\n", + CYCLE_TIMER_GET_CYCLES(pkt_ctr), + transmit_at_cycle, cycles_until_transmit, + transmit_at_time, (unsigned int)TICKS_TO_CYCLES(transmit_at_time), + presentation_time, (unsigned int)TICKS_TO_CYCLES(presentation_time)); +#ifdef DEBUG + if ( cycles_until_transmit > AMDTP_MAX_CYCLES_TO_TRANSMIT_EARLY + 1 ) + { + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "Way too early: CY=%04u, TC=%04u, CUT=%04d, TST=%011llu (%04u), TSP=%011llu (%04u)\n", + CYCLE_TIMER_GET_CYCLES(pkt_ctr), + transmit_at_cycle, cycles_until_transmit, + transmit_at_time, (unsigned int)TICKS_TO_CYCLES(transmit_at_time), + presentation_time, (unsigned int)TICKS_TO_CYCLES(presentation_time)); + } +#endif + // we are too early, send only an empty packet + return eCRV_EmptyPacket; + } + } + return eCRV_Invalid; +} + +enum StreamProcessor::eChildReturnValue +AmdtpTransmitStreamProcessor::generatePacketData ( + unsigned char *data, unsigned int *length ) +{ + if (m_data_buffer->readFrames(m_syt_interval, (char *)(data + 8))) + { + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "XMIT DATA: TSP= %011llu (%04u)\n", + m_last_timestamp, + (unsigned int)TICKS_TO_CYCLES(m_last_timestamp)); + return eCRV_OK; + } + else return eCRV_XRun; +} + +enum StreamProcessor::eChildReturnValue +AmdtpTransmitStreamProcessor::generateSilentPacketHeader ( + unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr ) +{ + struct iec61883_packet *packet = ( struct iec61883_packet * ) data; + debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, + "XMIT SILENT (cy %04d): CY=%04u, TSP=%011llu (%04u)\n", + CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp, + (unsigned int)TICKS_TO_CYCLES(m_last_timestamp)); + + packet->sid = m_local_node_id; + + packet->dbs = m_dimension; + packet->fn = 0; + packet->qpc = 0; + packet->sph = 0; + packet->reserved = 0; + packet->dbc = m_dbc; + packet->eoh1 = 2; + packet->fmt = IEC61883_FMT_AMDTP; + + *tag = IEC61883_TAG_WITH_CIP; + *sy = 0; + + m_dbc += fillNoDataPacketHeader(packet, length); + return eCRV_Packet; +} + +enum StreamProcessor::eChildReturnValue +AmdtpTransmitStreamProcessor::generateSilentPacketData ( + unsigned char *data, unsigned int *length ) +{ + return eCRV_OK; // no need to do anything +} + +enum StreamProcessor::eChildReturnValue +AmdtpTransmitStreamProcessor::generateEmptyPacketHeader ( + unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr ) +{ + struct iec61883_packet *packet = ( struct iec61883_packet * ) data; + debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, + "XMIT EMPTY (cy %04d): CY=%04u, TSP=%011llu (%04u)\n", + CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp, + (unsigned int)TICKS_TO_CYCLES(m_last_timestamp) ); + packet->sid = m_local_node_id; + + packet->dbs = m_dimension; + packet->fn = 0; + packet->qpc = 0; + packet->sph = 0; + packet->reserved = 0; + packet->dbc = m_dbc; + packet->eoh1 = 2; + packet->fmt = IEC61883_FMT_AMDTP; + + *tag = IEC61883_TAG_WITH_CIP; + *sy = 0; + + m_dbc += fillNoDataPacketHeader(packet, length); + return eCRV_OK; +} + +enum StreamProcessor::eChildReturnValue +AmdtpTransmitStreamProcessor::generateEmptyPacketData ( + unsigned char *data, unsigned int *length ) +{ + return eCRV_OK; // no need to do anything +} + +unsigned int AmdtpTransmitStreamProcessor::fillDataPacketHeader ( + struct iec61883_packet *packet, unsigned int* length, + uint32_t ts ) +{ + + packet->fdf = m_fdf; + + // convert the timestamp to SYT format + uint16_t timestamp_SYT = TICKS_TO_SYT ( ts ); + packet->syt = CondSwapToBus16 ( timestamp_SYT ); + + // FIXME: use a precomputed value here + *length = m_syt_interval*sizeof ( quadlet_t ) *m_dimension + 8; + + return m_syt_interval; +} + +unsigned int AmdtpTransmitStreamProcessor::fillNoDataPacketHeader ( + struct iec61883_packet *packet, unsigned int* length ) +{ + // no-data packets have syt=0xFFFF + // and (can) have the usual amount of events as dummy data + // DBC is not increased + packet->fdf = IEC61883_FDF_NODATA; + packet->syt = 0xffff; + +#if AMDTP_ALLOW_PAYLOAD_IN_NODATA_XMIT + if ( m_send_nodata_payload ) + { // no-data packets with payload (NOTE: DICE-II doesn't like that) + *length = 2*sizeof ( quadlet_t ) + m_syt_interval * m_dimension * sizeof ( quadlet_t ); + return m_syt_interval; + } else { // no-data packets without payload + *length = 2*sizeof ( quadlet_t ); + return 0; + } +#else + // no-data packets without payload + *length = 2*sizeof ( quadlet_t ); + return 0; +#endif +} + +unsigned int +AmdtpTransmitStreamProcessor::getSytInterval() { + switch (m_StreamProcessorManager.getNominalRate()) { + case 32000: + case 44100: + case 48000: + return 8; + case 88200: + case 96000: + return 16; + case 176400: + case 192000: + return 32; + default: + debugError("Unsupported rate: %d\n", m_StreamProcessorManager.getNominalRate()); + return 0; + } +} +unsigned int +AmdtpTransmitStreamProcessor::getFDF() { + switch (m_StreamProcessorManager.getNominalRate()) { + case 32000: return IEC61883_FDF_SFC_32KHZ; + case 44100: return IEC61883_FDF_SFC_44K1HZ; + case 48000: return IEC61883_FDF_SFC_48KHZ; + case 88200: return IEC61883_FDF_SFC_88K2HZ; + case 96000: return IEC61883_FDF_SFC_96KHZ; + case 176400: return IEC61883_FDF_SFC_176K4HZ; + case 192000: return IEC61883_FDF_SFC_192KHZ; + default: + debugError("Unsupported rate: %d\n", m_StreamProcessorManager.getNominalRate()); + return 0; + } +} + +bool AmdtpTransmitStreamProcessor::prepareChild() +{ + debugOutput ( DEBUG_LEVEL_VERBOSE, "Preparing (%p)...\n", this ); + m_syt_interval = getSytInterval(); + m_fdf = getFDF(); + + iec61883_cip_init ( + &m_cip_status, + IEC61883_FMT_AMDTP, + m_fdf, + m_StreamProcessorManager.getNominalRate(), + m_dimension, + m_syt_interval ); + + if (!initPortCache()) { + debugError("Could not init port cache\n"); + return false; + } + + return true; +} + +/* +* compose the event streams for the packets from the port buffers +*/ +bool AmdtpTransmitStreamProcessor::processWriteBlock ( char *data, + unsigned int nevents, unsigned int offset ) +{ + // update the variable parts of the cache + updatePortCache(); + + // encode audio data + switch(m_StreamProcessorManager.getAudioDataType()) { + case StreamProcessorManager::eADT_Int24: + encodeAudioPortsInt24((quadlet_t *)data, offset, nevents); + break; + case StreamProcessorManager::eADT_Float: + encodeAudioPortsFloat((quadlet_t *)data, offset, nevents); + break; + } + + // do midi ports + encodeMidiPorts((quadlet_t *)data, offset, nevents); + return true; +} + +bool +AmdtpTransmitStreamProcessor::transmitSilenceBlock( + char *data, unsigned int nevents, unsigned int offset) +{ + // no need to update the port cache when transmitting silence since + // no dynamic values are used to do so. + encodeAudioPortsSilence((quadlet_t *)data, offset, nevents); + encodeMidiPortsSilence((quadlet_t *)data, offset, nevents); + return true; +} + +/** + * @brief encodes all audio ports in the cache to events (silent data) + * @param data + * @param offset + * @param nevents + */ +void +AmdtpTransmitStreamProcessor::encodeAudioPortsSilence(quadlet_t *data, + unsigned int offset, + unsigned int nevents) +{ + unsigned int j; + quadlet_t *target_event; + unsigned int i; + + for (i = 0; i < m_nb_audio_ports; i++) { + target_event = (quadlet_t *)(data + i); + + for (j = 0;j < nevents; j += 1) + { + *target_event = CONDSWAPTOBUS32_CONST(0x40000000); + target_event += m_dimension; + } + } +} + +#ifdef __SSE2__ +//#if 0 +#include +#warning SSE2 build + +/** + * @brief mux all audio ports to events + * @param data + * @param offset + * @param nevents + */ +void +AmdtpTransmitStreamProcessor::encodeAudioPortsFloat(quadlet_t *data, + unsigned int offset, + unsigned int nevents) +{ + unsigned int j; + quadlet_t *target_event; + unsigned int i; + + float * client_buffers[4]; + float tmp_values[4] __attribute__ ((aligned (16))); + uint32_t tmp_values_int[4] __attribute__ ((aligned (16))); + + // prepare the scratch buffer + assert(m_scratch_buffer_size_bytes > nevents * 4); + memset(m_scratch_buffer, 0, nevents * 4); + + const __m128i label = _mm_set_epi32 (0x40000000, 0x40000000, 0x40000000, 0x40000000); + const __m128 mult = _mm_set_ps(AMDTP_FLOAT_MULTIPLIER, AMDTP_FLOAT_MULTIPLIER, AMDTP_FLOAT_MULTIPLIER, AMDTP_FLOAT_MULTIPLIER); + +#if AMDTP_CLIP_FLOATS + const __m128 v_max = _mm_set_ps(1.0, 1.0, 1.0, 1.0); + const __m128 v_min = _mm_set_ps(-1.0, -1.0, -1.0, -1.0); +#endif + + // this assumes that audio ports are sorted by position, + // and that there are no gaps + for (i = 0; i < m_nb_audio_ports-4; i += 4) { + struct _MBLA_port_cache *p; + + // get the port buffers + for (j=0; j<4; j++) { + p = &(m_audio_ports.at(i+j)); + if(p->buffer && p->enabled) { + client_buffers[j] = (float *) p->buffer; + client_buffers[j] += offset; + } else { + // if a port is disabled or has no valid + // buffer, use the scratch buffer (all zero's) + client_buffers[j] = (float *) m_scratch_buffer; + } + } + + // the base event for this position + target_event = (quadlet_t *)(data + i); + + // process the events + for (j=0;j < nevents; j += 1) + { + // read the values + tmp_values[0] = *(client_buffers[0]); + tmp_values[1] = *(client_buffers[1]); + tmp_values[2] = *(client_buffers[2]); + tmp_values[3] = *(client_buffers[3]); + + // now do the SSE based conversion/labeling + __m128 v_float = *((__m128*)tmp_values); + __m128i *target = (__m128i*)target_event; + __m128i v_int; + + // clip +#if AMDTP_CLIP_FLOATS + // do SSE clipping + v_float = _mm_max_ps(v_float, v_min); + v_float = _mm_min_ps(v_float, v_max); +#endif + + // multiply + v_float = _mm_mul_ps(v_float, mult); + // convert to signed integer + v_int = _mm_cvttps_epi32( v_float ); + // shift right 8 bits + v_int = _mm_srli_epi32( v_int, 8 ); + // label it + v_int = _mm_or_si128( v_int, label ); + + // do endian conversion (SSE is always little endian) + // do first swap + v_int = _mm_or_si128( _mm_slli_epi16( v_int, 8 ), _mm_srli_epi16( v_int, 8 ) ); + // do second swap + v_int = _mm_or_si128( _mm_slli_epi32( v_int, 16 ), _mm_srli_epi32( v_int, 16 ) ); + + // store the packed int + // (target misalignment is assumed since we don't know the m_dimension) + _mm_storeu_si128 (target, v_int); + + // increment the buffer pointers + client_buffers[0]++; + client_buffers[1]++; + client_buffers[2]++; + client_buffers[3]++; + + // go to next target event position + target_event += m_dimension; + } + } + + // do remaining ports + // NOTE: these can be time-SSE'd + for (; i < m_nb_audio_ports; i++) { + struct _MBLA_port_cache &p = m_audio_ports.at(i); + target_event = (quadlet_t *)(data + i); + assert(nevents + offset <= p.buffer_size ); + + if(p.buffer && p.enabled) { + float *buffer = (float *)(p.buffer); + buffer += offset; + + for (j = 0;j < nevents; j += 4) + { + // read the values + tmp_values[0] = *buffer; + buffer++; + tmp_values[1] = *buffer; + buffer++; + tmp_values[2] = *buffer; + buffer++; + tmp_values[3] = *buffer; + buffer++; + + // now do the SSE based conversion/labeling + __m128 v_float = *((__m128*)tmp_values); + __m128i v_int; + +#if AMDTP_CLIP_FLOATS + // do SSE clipping + v_float = _mm_max_ps(v_float, v_min); + v_float = _mm_min_ps(v_float, v_max); +#endif + + // multiply + v_float = _mm_mul_ps(v_float, mult); + // convert to signed integer + v_int = _mm_cvttps_epi32( v_float ); + // shift right 8 bits + v_int = _mm_srli_epi32( v_int, 8 ); + // label it + v_int = _mm_or_si128( v_int, label ); + + // do endian conversion (SSE is always little endian) + // do first swap + v_int = _mm_or_si128( _mm_slli_epi16( v_int, 8 ), _mm_srli_epi16( v_int, 8 ) ); + // do second swap + v_int = _mm_or_si128( _mm_slli_epi32( v_int, 16 ), _mm_srli_epi32( v_int, 16 ) ); + + // store the packed int + _mm_store_si128 ((__m128i *)(&tmp_values_int), v_int); + + // increment the buffer pointers + *target_event = tmp_values_int[0]; + target_event += m_dimension; + *target_event = tmp_values_int[1]; + target_event += m_dimension; + *target_event = tmp_values_int[2]; + target_event += m_dimension; + *target_event = tmp_values_int[3]; + target_event += m_dimension; + } + + // do the remainder of the events + for(;j < nevents; j += 1) { + float *in = (float *)buffer; +#if AMDTP_CLIP_FLOATS + if(*in > 1.0) *in=1.0; + if(*in < -1.0) *in=-1.0; +#endif + float v = (*in) * AMDTP_FLOAT_MULTIPLIER; + unsigned int tmp = ((int) v); + tmp = ( tmp >> 8 ) | 0x40000000; + *target_event = CondSwapToBus32((quadlet_t)tmp); + buffer++; + target_event += m_dimension; + } + + } else { + for (j = 0;j < nevents; j += 1) + { + // hardcoded byte swapped + *target_event = 0x00000040; + target_event += m_dimension; + } + } + } +} + + +/** + * @brief mux all audio ports to events + * @param data + * @param offset + * @param nevents + */ +void +AmdtpTransmitStreamProcessor::encodeAudioPortsInt24(quadlet_t *data, + unsigned int offset, + unsigned int nevents) +{ + unsigned int j; + quadlet_t *target_event; + unsigned int i; + + uint32_t *client_buffers[4]; + uint32_t tmp_values[4] __attribute__ ((aligned (16))); + + // prepare the scratch buffer + assert(m_scratch_buffer_size_bytes > nevents * 4); + memset(m_scratch_buffer, 0, nevents * 4); + + const __m128i label = _mm_set_epi32 (0x40000000, 0x40000000, 0x40000000, 0x40000000); + const __m128i mask = _mm_set_epi32 (0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF); + + // this assumes that audio ports are sorted by position, + // and that there are no gaps + for (i = 0; i < m_nb_audio_ports-4; i += 4) { + struct _MBLA_port_cache *p; + + // get the port buffers + for (j=0; j<4; j++) { + p = &(m_audio_ports.at(i+j)); + if(p->buffer && p->enabled) { + client_buffers[j] = (uint32_t *) p->buffer; + client_buffers[j] += offset; + } else { + // if a port is disabled or has no valid + // buffer, use the scratch buffer (all zero's) + client_buffers[j] = (uint32_t *) m_scratch_buffer; + } + } + + // the base event for this position + target_event = (quadlet_t *)(data + i); + + // process the events + for (j=0;j < nevents; j += 1) + { + // read the values + tmp_values[0] = *(client_buffers[0]); + tmp_values[1] = *(client_buffers[1]); + tmp_values[2] = *(client_buffers[2]); + tmp_values[3] = *(client_buffers[3]); + + // now do the SSE based conversion/labeling + __m128i *target = (__m128i*)target_event; + __m128i v_int = *((__m128i*)tmp_values);; + + // mask + v_int = _mm_and_si128( v_int, mask ); + // label it + v_int = _mm_or_si128( v_int, label ); + + // do endian conversion (SSE is always little endian) + // do first swap + v_int = _mm_or_si128( _mm_slli_epi16( v_int, 8 ), _mm_srli_epi16( v_int, 8 ) ); + // do second swap + v_int = _mm_or_si128( _mm_slli_epi32( v_int, 16 ), _mm_srli_epi32( v_int, 16 ) ); + + // store the packed int + // (target misalignment is assumed since we don't know the m_dimension) + _mm_storeu_si128 (target, v_int); + + // increment the buffer pointers + client_buffers[0]++; + client_buffers[1]++; + client_buffers[2]++; + client_buffers[3]++; + + // go to next target event position + target_event += m_dimension; + } + } + + // do remaining ports + // NOTE: these can be time-SSE'd + for (; i < m_nb_audio_ports; i++) { + struct _MBLA_port_cache &p = m_audio_ports.at(i); + target_event = (quadlet_t *)(data + i); + assert(nevents + offset <= p.buffer_size ); + + if(p.buffer && p.enabled) { + uint32_t *buffer = (uint32_t *)(p.buffer); + buffer += offset; + + for (j = 0;j < nevents; j += 4) + { + // read the values + tmp_values[0] = *buffer; + buffer++; + tmp_values[1] = *buffer; + buffer++; + tmp_values[2] = *buffer; + buffer++; + tmp_values[3] = *buffer; + buffer++; + + // now do the SSE based conversion/labeling + __m128i v_int = *((__m128i*)tmp_values);; + + // mask + v_int = _mm_and_si128( v_int, mask ); + // label it + v_int = _mm_or_si128( v_int, label ); + + // do endian conversion (SSE is always little endian) + // do first swap + v_int = _mm_or_si128( _mm_slli_epi16( v_int, 8 ), _mm_srli_epi16( v_int, 8 ) ); + // do second swap + v_int = _mm_or_si128( _mm_slli_epi32( v_int, 16 ), _mm_srli_epi32( v_int, 16 ) ); + + // store the packed int + _mm_store_si128 ((__m128i *)(&tmp_values), v_int); + + // increment the buffer pointers + *target_event = tmp_values[0]; + target_event += m_dimension; + *target_event = tmp_values[1]; + target_event += m_dimension; + *target_event = tmp_values[2]; + target_event += m_dimension; + *target_event = tmp_values[3]; + target_event += m_dimension; + } + + // do the remainder of the events + for(;j < nevents; j += 1) { + uint32_t in = (uint32_t)(*buffer); + *target_event = CondSwapToBus32((quadlet_t)((in & 0x00FFFFFF) | 0x40000000)); + buffer++; + target_event += m_dimension; + } + + } else { + for (j = 0;j < nevents; j += 1) + { + // hardcoded byte swapped + *target_event = 0x00000040; + target_event += m_dimension; + } + } + } +} + +#else + +/** + * @brief mux all audio ports to events + * @param data + * @param offset + * @param nevents + */ +void +AmdtpTransmitStreamProcessor::encodeAudioPortsInt24(quadlet_t *data, + unsigned int offset, + unsigned int nevents) +{ + unsigned int j; + quadlet_t *target_event; + unsigned int i; + + for (i = 0; i < m_nb_audio_ports; i++) { + struct _MBLA_port_cache &p = m_audio_ports.at(i); + target_event = (quadlet_t *)(data + i); + assert(nevents + offset <= p.buffer_size ); + + if(p.buffer && p.enabled) { + quadlet_t *buffer = (quadlet_t *)(p.buffer); + buffer += offset; + + for (j = 0;j < nevents; j += 1) + { + uint32_t in = (uint32_t)(*buffer); + *target_event = CondSwapToBus32((quadlet_t)((in & 0x00FFFFFF) | 0x40000000)); + buffer++; + target_event += m_dimension; + } + } else { + for (j = 0;j < nevents; j += 1) + { + *target_event = CONDSWAPTOBUS32_CONST(0x40000000); + target_event += m_dimension; + } + } + } +} + +/** + * @brief mux all audio ports to events + * @param data + * @param offset + * @param nevents + */ +void +AmdtpTransmitStreamProcessor::encodeAudioPortsFloat(quadlet_t *data, + unsigned int offset, + unsigned int nevents) +{ + unsigned int j; + quadlet_t *target_event; + unsigned int i; + + for (i = 0; i < m_nb_audio_ports; i++) { + struct _MBLA_port_cache &p = m_audio_ports.at(i); + target_event = (quadlet_t *)(data + i); + assert(nevents + offset <= p.buffer_size ); + + if(p.buffer && p.enabled) { + quadlet_t *buffer = (quadlet_t *)(p.buffer); + buffer += offset; + + for (j = 0;j < nevents; j += 1) + { + float *in = (float *)buffer; +#if AMDTP_CLIP_FLOATS + if(*in > 1.0) *in=1.0; + if(*in < -1.0) *in=-1.0; +#endif + float v = (*in) * AMDTP_FLOAT_MULTIPLIER; + unsigned int tmp = ((int) lrintf(v)); + + tmp = ( tmp >> 8 ) | 0x40000000; + *target_event = CondSwapToBus32((quadlet_t)tmp); + buffer++; + target_event += m_dimension; + } + } else { + for (j = 0;j < nevents; j += 1) + { + *target_event = CONDSWAPTOBUS32_CONST(0x40000000); + target_event += m_dimension; + } + } + } +} +#endif + +/** + * @brief encodes all midi ports in the cache to events (silence) + * @param data + * @param offset + * @param nevents + */ +void +AmdtpTransmitStreamProcessor::encodeMidiPortsSilence(quadlet_t *data, + unsigned int offset, + unsigned int nevents) +{ + quadlet_t *target_event; + unsigned int i,j; + + for (i = 0; i < m_nb_midi_ports; i++) { + struct _MIDI_port_cache &p = m_midi_ports.at(i); + + for (j = p.location;j < nevents; j += 8) { + target_event = (quadlet_t *) (data + ((j * m_dimension) + p.position)); + *target_event = CondSwapToBus32(IEC61883_AM824_SET_LABEL(0, IEC61883_AM824_LABEL_MIDI_NO_DATA)); + } + } +} + +/** + * @brief encodes all midi ports in the cache to events + * @param data + * @param offset + * @param nevents + */ +void +AmdtpTransmitStreamProcessor::encodeMidiPorts(quadlet_t *data, + unsigned int offset, + unsigned int nevents) +{ + quadlet_t *target_event; + unsigned int i,j; + + for (i = 0; i < m_nb_midi_ports; i++) { + struct _MIDI_port_cache &p = m_midi_ports.at(i); + if (p.buffer && p.enabled) { + uint32_t *buffer = (quadlet_t *)(p.buffer); + buffer += offset; + + for (j = p.location;j < nevents; j += 8) { + target_event = (quadlet_t *) (data + ((j * m_dimension) + p.position)); + + if ( *buffer & 0xFF000000 ) // we can send a byte + { + quadlet_t tmpval; + tmpval = ((*buffer)<<16) & 0x00FF0000; + tmpval = IEC61883_AM824_SET_LABEL(tmpval, IEC61883_AM824_LABEL_MIDI_1X); + *target_event = CondSwapToBus32(tmpval); + +// debugOutput ( DEBUG_LEVEL_VERBOSE, "MIDI port %s, pos=%u, loc=%u, nevents=%u, dim=%d\n", +// p.port->getName().c_str(), p.position, p.location, nevents, m_dimension ); +// debugOutput ( DEBUG_LEVEL_VERBOSE, "base=%p, target=%p, value=%08X\n", +// data, target_event, tmpval ); + } else { + // can't send a byte, either because there is no byte, + // or because this would exceed the maximum rate + // FIXME: this can be ifdef optimized since it's a constant + *target_event = CondSwapToBus32(IEC61883_AM824_SET_LABEL(0, IEC61883_AM824_LABEL_MIDI_NO_DATA)); + } + buffer+=8; + } + } else { + for (j = p.location;j < nevents; j += 8) { + target_event = (quadlet_t *)(data + ((j * m_dimension) + p.position)); + __builtin_prefetch(target_event, 1, 0); // prefetch events for write, no temporal locality + *target_event = CondSwapToBus32(IEC61883_AM824_SET_LABEL(0, IEC61883_AM824_LABEL_MIDI_NO_DATA)); + } + } + } +} + +bool +AmdtpTransmitStreamProcessor::initPortCache() { + // make use of the fact that audio ports are the first ports in + // the cluster as per AMDTP. so we can sort the ports by position + // and have very efficient lookups: + // m_float_ports.at(i).buffer -> audio stream i buffer + // for midi ports we simply cache all port info since they are (usually) not + // that numerous + m_nb_audio_ports = 0; + m_audio_ports.clear(); + + m_nb_midi_ports = 0; + m_midi_ports.clear(); + + for(PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + AmdtpPortInfo *pinfo=dynamic_cast(*it); + assert(pinfo); // this should not fail!! + + switch( pinfo->getFormat() ) + { + case AmdtpPortInfo::E_MBLA: + m_nb_audio_ports++; + break; + case AmdtpPortInfo::E_SPDIF: // still unimplemented + break; + case AmdtpPortInfo::E_Midi: + m_nb_midi_ports++; + break; + default: // ignore + break; + } + } + + unsigned int idx; + for (idx = 0; idx < m_nb_audio_ports; idx++) { + for(PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + AmdtpPortInfo *pinfo=dynamic_cast(*it); + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, + "idx %u: looking at port %s at position %u\n", + idx, (*it)->getName().c_str(), pinfo->getPosition()); + if(pinfo->getPosition() == idx) { + struct _MBLA_port_cache p; + p.port = dynamic_cast(*it); + if(p.port == NULL) { + debugError("Port is not an AmdtpAudioPort!\n"); + return false; + } + p.buffer = NULL; // to be filled by updatePortCache + #ifdef DEBUG + p.buffer_size = (*it)->getBufferSize(); + #endif + + m_audio_ports.push_back(p); + debugOutput(DEBUG_LEVEL_VERBOSE, + "Cached port %s at position %u\n", + p.port->getName().c_str(), idx); + goto next_index; + } + } + debugError("No MBLA port found for position %d\n", idx); + return false; +next_index: + continue; + } + + for(PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + AmdtpPortInfo *pinfo=dynamic_cast(*it); + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, + "idx %u: looking at port %s at position %u, location %u\n", + idx, (*it)->getName().c_str(), pinfo->getPosition(), pinfo->getLocation()); + if ((*it)->getPortType() == Port::E_Midi) { + struct _MIDI_port_cache p; + p.port = dynamic_cast(*it); + if(p.port == NULL) { + debugError("Port is not an AmdtpMidiPort!\n"); + return false; + } + p.position = pinfo->getPosition(); + p.location = pinfo->getLocation(); + p.buffer = NULL; // to be filled by updatePortCache + #ifdef DEBUG + p.buffer_size = (*it)->getBufferSize(); + #endif + + m_midi_ports.push_back(p); + debugOutput(DEBUG_LEVEL_VERBOSE, + "Cached port %s at position %u, location %u\n", + p.port->getName().c_str(), p.position, p.location); + } + } + + return true; +} + +void +AmdtpTransmitStreamProcessor::updatePortCache() { + unsigned int idx; + for (idx = 0; idx < m_nb_audio_ports; idx++) { + struct _MBLA_port_cache& p = m_audio_ports.at(idx); + AmdtpAudioPort *port = p.port; + p.buffer = port->getBufferAddress(); + p.enabled = !port->isDisabled(); + } + for (idx = 0; idx < m_nb_midi_ports; idx++) { + struct _MIDI_port_cache& p = m_midi_ports.at(idx); + AmdtpMidiPort *port = p.port; + p.buffer = port->getBufferAddress(); + p.enabled = !port->isDisabled(); + } +} + +} // end of namespace Streaming Index: /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.cpp =================================================================== --- /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.cpp (revision 1136) +++ /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.cpp (revision 1136) @@ -0,0 +1,505 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "config.h" + +#include "AmdtpReceiveStreamProcessor.h" +#include "AmdtpPort.h" +#include "../StreamProcessorManager.h" +#include "devicemanager.h" + +#include "libieee1394/ieee1394service.h" +#include "libieee1394/IsoHandlerManager.h" +#include "libieee1394/cycletimer.h" + +#include "libutil/ByteSwap.h" +#include + +namespace Streaming { + +/* --------------------- RECEIVE ----------------------- */ + +AmdtpReceiveStreamProcessor::AmdtpReceiveStreamProcessor(FFADODevice &parent, int dimension) + : StreamProcessor(parent, ePT_Receive) + , m_dimension( dimension ) + , m_nb_audio_ports( 0 ) + , m_nb_midi_ports( 0 ) + +{} + +unsigned int +AmdtpReceiveStreamProcessor::getSytInterval() { + switch (m_StreamProcessorManager.getNominalRate()) { + case 32000: + case 44100: + case 48000: + return 8; + case 88200: + case 96000: + return 16; + case 176400: + case 192000: + return 32; + default: + debugError("Unsupported rate: %d\n", m_StreamProcessorManager.getNominalRate()); + return 0; + } +} + +bool AmdtpReceiveStreamProcessor::prepareChild() { + debugOutput( DEBUG_LEVEL_VERBOSE, "Preparing (%p)...\n", this); + m_syt_interval = getSytInterval(); + + if (!initPortCache()) { + debugError("Could not init port cache\n"); + return false; + } + + return true; +} + + +/** + * Processes packet header to extract timestamps and so on + * @param data + * @param length + * @param channel + * @param tag + * @param sy + * @param pkt_ctr CTR value when packet was received + * @return + */ +enum StreamProcessor::eChildReturnValue +AmdtpReceiveStreamProcessor::processPacketHeader(unsigned char *data, unsigned int length, + unsigned char tag, unsigned char sy, + uint32_t pkt_ctr) +{ + struct iec61883_packet *packet = (struct iec61883_packet *) data; + assert(packet); + bool ok = (packet->syt != 0xFFFF) && + (packet->fdf != 0xFF) && + (packet->fmt == 0x10) && + (packet->dbs > 0) && + (length >= 2*sizeof(quadlet_t)); + if(ok) { + m_last_timestamp = sytRecvToFullTicks2((uint32_t)CondSwapFromBus16(packet->syt), pkt_ctr); + //#ifdef DEBUG + #if 0 + uint32_t now = m_1394service.getCycleTimer(); + + //=> convert the SYT to a full timestamp in ticks + uint64_t old_last_timestamp = sytRecvToFullTicks((uint32_t)CondSwapFromBus16(packet->syt), + CYCLE_TIMER_GET_CYCLES(pkt_ctr), now); + if(m_last_timestamp != old_last_timestamp) { + debugWarning("discepancy between timestamp calculations\n"); + } + #endif + } + return (ok ? eCRV_OK : eCRV_Invalid ); +} + +/** + * extract the data from the packet + * @pre the IEC61883 packet is valid according to isValidPacket + * @param data + * @param length + * @param channel + * @param tag + * @param sy + * @param pkt_ctr + * @return + */ +enum StreamProcessor::eChildReturnValue +AmdtpReceiveStreamProcessor::processPacketData(unsigned char *data, unsigned int length) { + struct iec61883_packet *packet = (struct iec61883_packet *) data; + assert(packet); + + unsigned int nevents=((length / sizeof (quadlet_t)) - 2)/packet->dbs; + + // we have to keep in mind that there are also + // some packets buffered by the ISO layer, + // at most x=m_handler->getWakeupInterval() + // these contain at most x*syt_interval + // frames, meaning that we might receive + // this packet x*syt_interval*ticks_per_frame + // later than expected (the real receive time) + #ifdef DEBUG + if(isRunning()) { + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "STMP: %lluticks | syt_interval=%d, tpf=%f\n", + m_last_timestamp, m_syt_interval, getTicksPerFrame()); + } + + // check whether nevents is a multiple of 8. + if (nevents & 0x7) { + debugError("Invalid nevents value for AMDTP (%u)\n", nevents); + } + #endif + + if(m_data_buffer->writeFrames(nevents, (char *)(data+8), m_last_timestamp)) { + return eCRV_OK; + } else { + return eCRV_XRun; + } +} + +/*********************************************** + * Encoding/Decoding API * + ***********************************************/ +/** + * @brief write received events to the stream ringbuffers. + */ +bool AmdtpReceiveStreamProcessor::processReadBlock(char *data, + unsigned int nevents, unsigned int offset) +{ + debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, + "(%p)->processReadBlock(%u, %u)\n", + this,nevents,offset); + + // update the variable parts of the cache + updatePortCache(); + + // decode audio data + switch(m_StreamProcessorManager.getAudioDataType()) { + case StreamProcessorManager::eADT_Int24: + decodeAudioPortsInt24((quadlet_t *)data, offset, nevents); + break; + case StreamProcessorManager::eADT_Float: + decodeAudioPortsFloat((quadlet_t *)data, offset, nevents); + break; + } + + // do midi ports + decodeMidiPorts((quadlet_t *)data, offset, nevents); + return true; +} + +//#ifdef __SSE2__ +#if 0 // SSE code is not ready yet +#include +#warning SSE2 build + +/** + * @brief demux events to all audio ports (int24) + * @param data + * @param offset + * @param nevents + */ +void +AmdtpReceiveStreamProcessor::decodeAudioPortsInt24(quadlet_t *data, + unsigned int offset, + unsigned int nevents) +{ + unsigned int j; + quadlet_t *target_event; + unsigned int i; + + for (i = 0; i < m_nb_audio_ports; i++) { + struct _MBLA_port_cache &p = m_audio_ports.at(i); + target_event = (quadlet_t *)(data + i); + assert(nevents + offset <= p.buffer_size ); + + if(p.buffer && p.enabled) { + quadlet_t *buffer = (quadlet_t *)(p.buffer); + buffer += offset; + + for(j = 0; j < nevents; j += 1) { + *(buffer)=(CondSwapFromBus32((*target_event) ) & 0x00FFFFFF); + buffer++; + target_event+=m_dimension; + } + } + } +} + +/** + * @brief demux events to all audio ports (float) + * @param data + * @param offset + * @param nevents + */ +void +AmdtpReceiveStreamProcessor::decodeAudioPortsFloat(quadlet_t *data, + unsigned int offset, + unsigned int nevents) +{ + unsigned int j; + quadlet_t *target_event; + unsigned int i; + const float multiplier = 1.0f / (float)(0x7FFFFF); + + for (i = 0; i < m_nb_audio_ports; i++) { + struct _MBLA_port_cache &p = m_audio_ports.at(i); + target_event = (quadlet_t *)(data + i); + assert(nevents + offset <= p.buffer_size ); + + if(p.buffer && p.enabled) { + float *buffer = (float *)(p.buffer); + buffer += offset; + + for(j = 0; j < nevents; j += 1) { + unsigned int v = CondSwapFromBus32(*target_event) & 0x00FFFFFF; + // sign-extend highest bit of 24-bit int + int tmp = (int)(v << 8) / 256; + *buffer = tmp * multiplier; + buffer++; + target_event+=m_dimension; + } + } + } +} + +#else +/** + * @brief demux events to all audio ports (int24) + * @param data + * @param offset + * @param nevents + */ +void +AmdtpReceiveStreamProcessor::decodeAudioPortsInt24(quadlet_t *data, + unsigned int offset, + unsigned int nevents) +{ + unsigned int j; + quadlet_t *target_event; + unsigned int i; + + for (i = 0; i < m_nb_audio_ports; i++) { + struct _MBLA_port_cache &p = m_audio_ports.at(i); + target_event = (quadlet_t *)(data + i); + assert(nevents + offset <= p.buffer_size ); + + if(p.buffer && p.enabled) { + quadlet_t *buffer = (quadlet_t *)(p.buffer); + buffer += offset; + + for(j = 0; j < nevents; j += 1) { + *(buffer)=(CondSwapFromBus32((*target_event) ) & 0x00FFFFFF); + buffer++; + target_event+=m_dimension; + } + } + } +} + +/** + * @brief demux events to all audio ports (float) + * @param data + * @param offset + * @param nevents + */ +void +AmdtpReceiveStreamProcessor::decodeAudioPortsFloat(quadlet_t *data, + unsigned int offset, + unsigned int nevents) +{ + unsigned int j; + quadlet_t *target_event; + unsigned int i; + const float multiplier = 1.0f / (float)(0x7FFFFF); + + for (i = 0; i < m_nb_audio_ports; i++) { + struct _MBLA_port_cache &p = m_audio_ports.at(i); + target_event = (quadlet_t *)(data + i); + assert(nevents + offset <= p.buffer_size ); + + if(p.buffer && p.enabled) { + float *buffer = (float *)(p.buffer); + buffer += offset; + + for(j = 0; j < nevents; j += 1) { + unsigned int v = CondSwapFromBus32(*target_event) & 0x00FFFFFF; + // sign-extend highest bit of 24-bit int + int tmp = (int)(v << 8) / 256; + *buffer = tmp * multiplier; + buffer++; + target_event+=m_dimension; + } + } + } +} + +#endif + +/** + * @brief decode all midi ports in the cache from events + * @param data + * @param offset + * @param nevents + */ +void +AmdtpReceiveStreamProcessor::decodeMidiPorts(quadlet_t *data, + unsigned int offset, + unsigned int nevents) +{ + quadlet_t *target_event; + quadlet_t sample_int; + unsigned int i,j; + + for (i = 0; i < m_nb_midi_ports; i++) { + struct _MIDI_port_cache &p = m_midi_ports.at(i); + if (p.buffer && p.enabled) { + uint32_t *buffer = (quadlet_t *)(p.buffer); + buffer += offset; + + for (j = p.location;j < nevents; j += 8) { + target_event = (quadlet_t *) (data + ((j * m_dimension) + p.position)); + sample_int=CondSwapFromBus32(*target_event); + // FIXME: this assumes that 2X and 3X speed isn't used, + // because only the 1X slot is put into the ringbuffer + if(IEC61883_AM824_GET_LABEL(sample_int) != IEC61883_AM824_LABEL_MIDI_NO_DATA) { + sample_int=(sample_int >> 16) & 0x000000FF; + sample_int |= 0x01000000; // flag that there is a midi event present + *buffer = sample_int; + debugOutput(DEBUG_LEVEL_VERBOSE, "Received midi byte %08X on port %p index %d\n", sample_int, p, j-p.location); + } else { + // make sure no event is received + *buffer = 0; + } + buffer+=8; + } + } + } +} + +bool +AmdtpReceiveStreamProcessor::initPortCache() { + // make use of the fact that audio ports are the first ports in + // the cluster as per AMDTP. so we can sort the ports by position + // and have very efficient lookups: + // m_float_ports.at(i).buffer -> audio stream i buffer + // for midi ports we simply cache all port info since they are (usually) not + // that numerous + m_nb_audio_ports = 0; + m_audio_ports.clear(); + + m_nb_midi_ports = 0; + m_midi_ports.clear(); + + for(PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + AmdtpPortInfo *pinfo=dynamic_cast(*it); + assert(pinfo); // this should not fail!! + + switch( pinfo->getFormat() ) + { + case AmdtpPortInfo::E_MBLA: + m_nb_audio_ports++; + break; + case AmdtpPortInfo::E_SPDIF: // still unimplemented + break; + case AmdtpPortInfo::E_Midi: + m_nb_midi_ports++; + break; + default: // ignore + break; + } + } + + unsigned int idx; + for (idx = 0; idx < m_nb_audio_ports; idx++) { + for(PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + AmdtpPortInfo *pinfo=dynamic_cast(*it); + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, + "idx %u: looking at port %s at position %u\n", + idx, (*it)->getName().c_str(), pinfo->getPosition()); + if(pinfo->getPosition() == idx) { + struct _MBLA_port_cache p; + p.port = dynamic_cast(*it); + if(p.port == NULL) { + debugError("Port is not an AmdtpAudioPort!\n"); + return false; + } + p.buffer = NULL; // to be filled by updatePortCache + #ifdef DEBUG + p.buffer_size = (*it)->getBufferSize(); + #endif + + m_audio_ports.push_back(p); + debugOutput(DEBUG_LEVEL_VERBOSE, + "Cached port %s at position %u\n", + p.port->getName().c_str(), idx); + goto next_index; + } + } + debugError("No MBLA port found for position %d\n", idx); + return false; +next_index: + continue; + } + + for(PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + AmdtpPortInfo *pinfo=dynamic_cast(*it); + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, + "idx %u: looking at port %s at position %u, location %u\n", + idx, (*it)->getName().c_str(), pinfo->getPosition(), pinfo->getLocation()); + if ((*it)->getPortType() == Port::E_Midi) { + struct _MIDI_port_cache p; + p.port = dynamic_cast(*it); + if(p.port == NULL) { + debugError("Port is not an AmdtpMidiPort!\n"); + return false; + } + p.position = pinfo->getPosition(); + p.location = pinfo->getLocation(); + p.buffer = NULL; // to be filled by updatePortCache + #ifdef DEBUG + p.buffer_size = (*it)->getBufferSize(); + #endif + + m_midi_ports.push_back(p); + debugOutput(DEBUG_LEVEL_VERBOSE, + "Cached port %s at position %u, location %u\n", + p.port->getName().c_str(), p.position, p.location); + } + } + + return true; +} + +void +AmdtpReceiveStreamProcessor::updatePortCache() { + unsigned int idx; + for (idx = 0; idx < m_nb_audio_ports; idx++) { + struct _MBLA_port_cache& p = m_audio_ports.at(idx); + AmdtpAudioPort *port = p.port; + p.buffer = port->getBufferAddress(); + p.enabled = !port->isDisabled(); + } + for (idx = 0; idx < m_nb_midi_ports; idx++) { + struct _MIDI_port_cache& p = m_midi_ports.at(idx); + AmdtpMidiPort *port = p.port; + p.buffer = port->getBufferAddress(); + p.enabled = !port->isDisabled(); + } +} + +} // end of namespace Streaming Index: /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpBufferOps.h =================================================================== --- /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpBufferOps.h (revision 864) +++ /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpBufferOps.h (revision 864) @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_AMDTPBUFFEROPS__ +#define __FFADO_AMDTPBUFFEROPS__ + +#include +// to check for SSE etc... +#include "config.h" + +#include + +#define AMDTP_FLOAT_MULTIPLIER 2147483392.0 + +#ifdef __SSE2__ +//#if 0 +#include +#warning SSE2 build + +//static inline void +void +convertFromFloatAndLabelAsMBLA(quadlet_t *data, unsigned int nb_elements) +{ + // Work input until data reaches 16 byte alignment + while ((((unsigned long)data) & 0xF) && nb_elements > 0) { + float *in = (float *)data; + float v = (*in) * AMDTP_FLOAT_MULTIPLIER; + unsigned int tmp = ((int) v); + tmp = ( tmp >> 8 ) | 0x40000000; + *data = (quadlet_t)tmp; + data++; + nb_elements--; + } + assert((((unsigned long)data) & 0xF) == 0); + + // now do the SSE based conversion/labeling + __m128i v_int; + __m128i label = _mm_set_epi32 (0x40000000, 0x40000000, 0x40000000, 0x40000000); + __m128 mult = _mm_set_ps(AMDTP_FLOAT_MULTIPLIER, AMDTP_FLOAT_MULTIPLIER, AMDTP_FLOAT_MULTIPLIER, AMDTP_FLOAT_MULTIPLIER); + __m128 v_float; + while(nb_elements >= 4) { + float *in = (float *)data; + // load the data into the vector unit + v_float = _mm_load_ps(in); + // multiply + v_float = _mm_mul_ps(v_float, mult); + // convert to signed integer + v_int = _mm_cvttps_epi32( v_float ); + // shift right 8 bits + v_int = _mm_srli_epi32( v_int, 8 ); + // label it + v_int = _mm_or_si128( v_int, label ); + // store result + _mm_store_si128 ((__m128i*)data, v_int); + + data += 4; + nb_elements -= 4; + } + + // and do the remaining ones + while (nb_elements > 0) { + float *in = (float *)data; + float v = (*in) * AMDTP_FLOAT_MULTIPLIER; + unsigned int tmp = ((int) v); + tmp = ( tmp >> 8 ) | 0x40000000; + *data = (quadlet_t)tmp; + data++; + nb_elements--; + } +} + +//static inline void +void +convertFromInt24AndLabelAsMBLA(quadlet_t *data, unsigned int nb_elements) +{ + // Work input until data reaches 16 byte alignment + while ((((unsigned long)data) & 0xF) && nb_elements > 0) { + uint32_t in = (uint32_t)(*data); + *data = (quadlet_t)((in & 0x00FFFFFF) | 0x40000000); + data++; + nb_elements--; + } + assert((((unsigned long)data) & 0xF) == 0); + + // now do the SSE based labeling + __m128i v; + const __m128i mask = _mm_set_epi32 (0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF, 0x00FFFFFF); + const __m128i label = _mm_set_epi32 (0x40000000, 0x40000000, 0x40000000, 0x40000000); + while(nb_elements >= 4) { + // load the data into the vector unit + v = _mm_load_si128((__m128i*)data); + // mask + v = _mm_and_si128( v, mask ); + // label + v = _mm_or_si128( v, label ); + // store result + _mm_store_si128 ((__m128i*)data, v); + + data += 4; + nb_elements -= 4; + } + + // and do the remaining ones + while (nb_elements > 0) { + uint32_t in = (uint32_t)(*data); + *data = (quadlet_t)((in & 0x00FFFFFF) | 0x40000000); + data++; + nb_elements--; + } +} + +#else + +//static inline void +void +convertFromFloatAndLabelAsMBLA(quadlet_t *data, unsigned int nb_elements) +{ + unsigned int i=0; + for(; i> 8 ) | 0x40000000; + *data = (quadlet_t)tmp; + data++; + } +} + +//static inline void +void +convertFromInt24AndLabelAsMBLA(quadlet_t *data, unsigned int nb_elements) +{ + unsigned int i=0; + for(; i. + * + */ + +#include "AmdtpPortInfo.h" +#include + + +namespace Streaming { + + +} // end of namespace Streaming Index: /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpTransmitStreamProcessor.h =================================================================== --- /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpTransmitStreamProcessor.h (revision 1034) +++ /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpTransmitStreamProcessor.h (revision 1034) @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_AMDTPTRANSMITSTREAMPROCESSOR__ +#define __FFADO_AMDTPTRANSMITSTREAMPROCESSOR__ + +/** + * This class implements IEC61883-6 / AM824 / AMDTP based streaming + */ + +#include "debugmodule/debugmodule.h" + +#include "../generic/StreamProcessor.h" +#include "../util/cip.h" + +#include +#include + +#define AMDTP_MAX_PACKET_SIZE 2048 + +#define IEC61883_STREAM_TYPE_MIDI 0x0D +#define IEC61883_STREAM_TYPE_SPDIF 0x00 +#define IEC61883_STREAM_TYPE_MBLA 0x06 + +#define IEC61883_AM824_LABEL_MASK 0xFF000000 +#define IEC61883_AM824_GET_LABEL(x) (((x) & 0xFF000000) >> 24) +#define IEC61883_AM824_SET_LABEL(x,y) ((x) | ((y)<<24)) + +#define IEC61883_AM824_LABEL_MIDI_NO_DATA 0x80 +#define IEC61883_AM824_LABEL_MIDI_1X 0x81 +#define IEC61883_AM824_LABEL_MIDI_2X 0x82 +#define IEC61883_AM824_LABEL_MIDI_3X 0x83 + +namespace Streaming { + +class Port; +class AmdtpAudioPort; +class AmdtpMidiPort; + +/*! +\brief The Base Class for an AMDTP transmit stream processor + + This class implements a TransmitStreamProcessor that multiplexes Ports + into AMDTP streams. + +*/ +class AmdtpTransmitStreamProcessor + : public StreamProcessor +{ + +public: + /** + * Create a AMDTP transmit StreamProcessor + * @param port 1394 port + * @param framerate frame rate + * @param dimension number of substreams in the ISO stream + * (midi-muxed is only one stream) + */ + AmdtpTransmitStreamProcessor(FFADODevice &parent, int dimension); + virtual ~AmdtpTransmitStreamProcessor() {}; + + enum eChildReturnValue generatePacketHeader(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr); + enum eChildReturnValue generatePacketData(unsigned char *data, unsigned int *length); + enum eChildReturnValue generateEmptyPacketHeader(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr); + enum eChildReturnValue generateEmptyPacketData(unsigned char *data, unsigned int *length); + enum eChildReturnValue generateSilentPacketHeader(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr); + enum eChildReturnValue generateSilentPacketData(unsigned char *data, unsigned int *length); + virtual bool prepareChild(); + +#if AMDTP_ALLOW_PAYLOAD_IN_NODATA_XMIT +public: + void sendPayloadForNoDataPackets(bool b) {m_send_nodata_payload = b;}; +#endif + +public: + virtual unsigned int getEventSize() + {return 4;}; + virtual unsigned int getMaxPacketSize() + {return 4 * (2 + getSytInterval() * m_dimension);}; + virtual unsigned int getEventsPerFrame() + { return m_dimension; }; + virtual unsigned int getNominalFramesPerPacket() + {return getSytInterval();}; + +protected: + bool processWriteBlock(char *data, unsigned int nevents, unsigned int offset); + bool transmitSilenceBlock(char *data, unsigned int nevents, unsigned int offset); + +private: + unsigned int fillNoDataPacketHeader(struct iec61883_packet *packet, unsigned int* length); + unsigned int fillDataPacketHeader(struct iec61883_packet *packet, unsigned int* length, uint32_t ts); + + int transmitBlock(char *data, unsigned int nevents, + unsigned int offset); + + void encodeAudioPortsSilence(quadlet_t *data, unsigned int offset, unsigned int nevents); + void encodeAudioPortsFloat(quadlet_t *data, unsigned int offset, unsigned int nevents); + void encodeAudioPortsInt24(quadlet_t *data, unsigned int offset, unsigned int nevents); + void encodeMidiPortsSilence(quadlet_t *data, unsigned int offset, unsigned int nevents); + void encodeMidiPorts(quadlet_t *data, unsigned int offset, unsigned int nevents); + + unsigned int getFDF(); + unsigned int getSytInterval(); + + struct iec61883_cip m_cip_status; + int m_dimension; + unsigned int m_syt_interval; + int m_fdf; + unsigned int m_dbc; + +#if AMDTP_ALLOW_PAYLOAD_IN_NODATA_XMIT +private: + bool m_send_nodata_payload; +#endif + +private: // local port caching for performance + struct _MBLA_port_cache { + AmdtpAudioPort* port; + void* buffer; + bool enabled; +#ifdef DEBUG + unsigned int buffer_size; +#endif + }; + std::vector m_audio_ports; + unsigned int m_nb_audio_ports; + + struct _MIDI_port_cache { + AmdtpMidiPort* port; + void* buffer; + bool enabled; + unsigned int position; + unsigned int location; +#ifdef DEBUG + unsigned int buffer_size; +#endif + }; + std::vector m_midi_ports; + unsigned int m_nb_midi_ports; + + bool initPortCache(); + void updatePortCache(); +}; + +} // end of namespace Streaming + +#endif /* __FFADO_AMDTPTRANSMITSTREAMPROCESSOR__ */ + Index: /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.h =================================================================== --- /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.h (revision 1035) +++ /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.h (revision 1035) @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_AMDTPRECEIVESTREAMPROCESSOR__ +#define __FFADO_AMDTPRECEIVESTREAMPROCESSOR__ + +/** + * This class implements IEC61883-6 / AM824 / AMDTP based streaming + */ + +#include "debugmodule/debugmodule.h" + +#include "../generic/StreamProcessor.h" +#include "../util/cip.h" + +#include +#include + +#define AMDTP_MAX_PACKET_SIZE 2048 + +#define IEC61883_STREAM_TYPE_MIDI 0x0D +#define IEC61883_STREAM_TYPE_SPDIF 0x00 +#define IEC61883_STREAM_TYPE_MBLA 0x06 + +#define IEC61883_AM824_LABEL_MASK 0xFF000000 +#define IEC61883_AM824_GET_LABEL(x) (((x) & 0xFF000000) >> 24) +#define IEC61883_AM824_SET_LABEL(x,y) ((x) | ((y)<<24)) + +#define IEC61883_AM824_LABEL_MIDI_NO_DATA 0x80 +#define IEC61883_AM824_LABEL_MIDI_1X 0x81 +#define IEC61883_AM824_LABEL_MIDI_2X 0x82 +#define IEC61883_AM824_LABEL_MIDI_3X 0x83 + +namespace Streaming { + +class Port; +class AmdtpAudioPort; +class AmdtpMidiPort; +/*! +\brief The Base Class for an AMDTP receive stream processor + + This class implements a ReceiveStreamProcessor that demultiplexes + AMDTP streams into Ports. + +*/ +class AmdtpReceiveStreamProcessor + : public StreamProcessor +{ + +public: + /** + * Create a AMDTP receive StreamProcessor + * @param port 1394 port + * @param dimension number of substreams in the ISO stream + * (midi-muxed is only one stream) + */ + AmdtpReceiveStreamProcessor(FFADODevice &parent, int dimension); + virtual ~AmdtpReceiveStreamProcessor() {}; + + enum eChildReturnValue processPacketHeader(unsigned char *data, unsigned int length, + unsigned char tag, unsigned char sy, + uint32_t pkt_ctr); + enum eChildReturnValue processPacketData(unsigned char *data, unsigned int length); + + virtual bool prepareChild(); + +public: + virtual unsigned int getEventSize() + {return 4;}; + virtual unsigned int getMaxPacketSize() + {return 4 * (2 + getSytInterval() * m_dimension);}; + virtual unsigned int getEventsPerFrame() + { return m_dimension; }; + virtual unsigned int getNominalFramesPerPacket() + {return getSytInterval();}; + +protected: + bool processReadBlock(char *data, unsigned int nevents, unsigned int offset); + +private: + void decodeAudioPortsFloat(quadlet_t *data, unsigned int offset, unsigned int nevents); + void decodeAudioPortsInt24(quadlet_t *data, unsigned int offset, unsigned int nevents); + void decodeMidiPorts(quadlet_t *data, unsigned int offset, unsigned int nevents); + + unsigned int getSytInterval(); + + int m_dimension; + unsigned int m_syt_interval; + +private: // local port caching for performance + struct _MBLA_port_cache { + AmdtpAudioPort* port; + void* buffer; + bool enabled; +#ifdef DEBUG + unsigned int buffer_size; +#endif + }; + std::vector m_audio_ports; + unsigned int m_nb_audio_ports; + + struct _MIDI_port_cache { + AmdtpMidiPort* port; + void* buffer; + bool enabled; + unsigned int position; + unsigned int location; +#ifdef DEBUG + unsigned int buffer_size; +#endif + }; + std::vector m_midi_ports; + unsigned int m_nb_midi_ports; + + bool initPortCache(); + void updatePortCache(); +}; + + +} // end of namespace Streaming + +#endif /* __FFADO_AMDTPRECEIVESTREAMPROCESSOR__ */ + Index: /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpPort.cpp =================================================================== --- /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpPort.cpp (revision 864) +++ /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpPort.cpp (revision 864) @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "AmdtpPort.h" +#include + +namespace Streaming { + +} // end of namespace Streaming Index: /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpPortInfo.h =================================================================== --- /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpPortInfo.h (revision 864) +++ /branches/libffado-2.0/src/libstreaming/amdtp/AmdtpPortInfo.h (revision 864) @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_AMDTPPORTINFO__ +#define __FFADO_AMDTPPORTINFO__ + +#include "debugmodule/debugmodule.h" +#include + +namespace Streaming { +/*! +\brief Class containing the stream information for an AMDTP channel + + Contains the information that maps the port to an AMDTP stream position (i.e. channel) + this allows the AMDTP stream demultiplexer to find the channel associated + to this port. + +*/ +class AmdtpPortInfo { + +public: + /** + * Sometimes a channel can have multiple formats, depending on the + * device configuration (e.g. an SPDIF port could be plain audio in 24bit integer + * or AC3 passthrough in IEC compliant frames.) + * + * This kind of enum allows to discriminate these formats when decoding + * If all channels always have the same format, you won't be needing this + */ + enum E_Formats { + E_MBLA, ///< multibit linear audio + E_Midi, ///< midi + E_SPDIF,///< IEC.... format + }; + + AmdtpPortInfo( int position, int location, enum E_Formats format) + : m_position(position), m_location(location), m_format(format) + {}; + virtual ~AmdtpPortInfo() {}; + + + unsigned int getLocation() {return m_location;}; + unsigned int getPosition() {return m_position;}; + enum E_Formats getFormat() {return m_format;}; + +protected: + unsigned int m_position; + unsigned int m_location; + enum E_Formats m_format; + +}; + +} // end of namespace Streaming + +#endif /* __FFADO_AMDTPPORTINFO__ */ Index: /branches/libffado-2.0/src/libstreaming/StreamProcessorManager.cpp =================================================================== --- /branches/libffado-2.0/src/libstreaming/StreamProcessorManager.cpp (revision 1165) +++ /branches/libffado-2.0/src/libstreaming/StreamProcessorManager.cpp (revision 1165) @@ -0,0 +1,1299 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "config.h" + +#include "StreamProcessorManager.h" +#include "generic/StreamProcessor.h" +#include "generic/Port.h" +#include "libieee1394/cycletimer.h" + +#include "libutil/Time.h" + +#include +#include +#include + +namespace Streaming { + +IMPL_DEBUG_MODULE( StreamProcessorManager, StreamProcessorManager, DEBUG_LEVEL_VERBOSE ); + +StreamProcessorManager::StreamProcessorManager() + : m_is_slave( false ) + , m_SyncSource(NULL) + , m_xrun_happened( false ) + , m_activity_wait_timeout_usec( 1000*1000 ) + , m_nb_buffers( 0 ) + , m_period( 0 ) + , m_audio_datatype( eADT_Float ) + , m_nominal_framerate ( 0 ) + , m_xruns(0) + , m_shutdown_needed(false) + , m_nbperiods(0) + , m_WaitLock( new Util::PosixMutex ) +{ + addOption(Util::OptionContainer::Option("slaveMode",false)); + sem_init(&m_activity_semaphore, 0, 0); +} + +StreamProcessorManager::StreamProcessorManager(unsigned int period, unsigned int framerate, unsigned int nb_buffers) + : m_is_slave( false ) + , m_SyncSource(NULL) + , m_xrun_happened( false ) + , m_activity_wait_timeout_usec( 1000*1000 ) + , m_nb_buffers(nb_buffers) + , m_period(period) + , m_audio_datatype( eADT_Float ) + , m_nominal_framerate ( framerate ) + , m_xruns(0) + , m_shutdown_needed(false) + , m_nbperiods(0) + , m_WaitLock( new Util::PosixMutex ) +{ + addOption(Util::OptionContainer::Option("slaveMode",false)); + sem_init(&m_activity_semaphore, 0, 0); +} + +StreamProcessorManager::~StreamProcessorManager() { + sem_post(&m_activity_semaphore); + sem_destroy(&m_activity_semaphore); + delete m_WaitLock; +} + +void +StreamProcessorManager::handleBusReset() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) Handle bus reset...\n", this); + + // FIXME: we request shutdown for now. + m_shutdown_needed=true; + + // note that all receive streams are gone once a device is unplugged + + // synchronize with the wait lock + Util::MutexLockHelper lock(*m_WaitLock); + + debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) got wait lock...\n", this); + // cause all SP's to bail out + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) + { + (*it)->handleBusReset(); + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) + { + (*it)->handleBusReset(); + } +} + +void +StreamProcessorManager::signalActivity() +{ + sem_post(&m_activity_semaphore); + debugOutputExtreme(DEBUG_LEVEL_VERBOSE,"%p activity\n", this); +} + +enum StreamProcessorManager::eActivityResult +StreamProcessorManager::waitForActivity() +{ + debugOutputExtreme(DEBUG_LEVEL_VERBOSE,"%p waiting for activity\n", this); + struct timespec ts; + int result; + + if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { + debugError("clock_gettime failed\n"); + return eAR_Error; + } + long long int timeout_nsec=0; + int timeout_sec = 0; + if (m_activity_wait_timeout_usec >= 0) { + timeout_nsec = m_activity_wait_timeout_usec * 1000LL; + timeout_sec = 0; + while(timeout_nsec >= 1000000000LL) { + timeout_sec += 1; + timeout_nsec -= 1000000000LL; + } + ts.tv_nsec += timeout_nsec; + ts.tv_sec += timeout_sec; + } + + if (m_activity_wait_timeout_usec >= 0) { + result = sem_timedwait(&m_activity_semaphore, &ts); + } else { + result = sem_wait(&m_activity_semaphore); + } + + if(result != 0) { + if (errno == ETIMEDOUT) { + debugOutput(DEBUG_LEVEL_VERBOSE, + "(%p) sem_timedwait() timed out (result=%d)\n", + this, result); + return eAR_Timeout; + } else if (errno == EINTR) { + debugOutput(DEBUG_LEVEL_VERBOSE, + "(%p) sem_[timed]wait() interrupted by signal (result=%d)\n", + this, result); + return eAR_Interrupted; + } else { + debugError("(%p) sem_[timed]wait error (result=%d errno=%d)\n", + this, result, errno); + debugError("(%p) timeout_sec=%d timeout_nsec=%lld ts.sec=%d ts.nsec=%lld\n", + this, timeout_sec, timeout_nsec, ts.tv_sec, ts.tv_nsec); + return eAR_Error; + } + } + + debugOutputExtreme(DEBUG_LEVEL_VERBOSE,"%p got activity\n", this); + return eAR_Activity; +} + +/** + * Registers \ref processor with this manager. + * + * also registers it with the isohandlermanager + * + * be sure to call isohandlermanager->init() first! + * and be sure that the processors are also ->init()'ed + * + * @param processor + * @return true if successfull + */ +bool StreamProcessorManager::registerProcessor(StreamProcessor *processor) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Registering processor (%p)\n",processor); + assert(processor); + if (processor->getType() == StreamProcessor::ePT_Receive) { + processor->setVerboseLevel(getDebugLevel()); // inherit debug level + m_ReceiveProcessors.push_back(processor); + return true; + } + + if (processor->getType() == StreamProcessor::ePT_Transmit) { + processor->setVerboseLevel(getDebugLevel()); // inherit debug level + m_TransmitProcessors.push_back(processor); + return true; + } + + debugFatal("Unsupported processor type!\n"); + return false; +} + +bool StreamProcessorManager::unregisterProcessor(StreamProcessor *processor) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Unregistering processor (%p)\n",processor); + assert(processor); + + if (processor->getType()==StreamProcessor::ePT_Receive) { + + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) + { + if ( *it == processor ) { + if (*it == m_SyncSource) { + debugOutput(DEBUG_LEVEL_VERBOSE, "unregistering sync source\n"); + m_SyncSource = NULL; + } + m_ReceiveProcessors.erase(it); + return true; + } + } + } + + if (processor->getType()==StreamProcessor::ePT_Transmit) { + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) + { + if ( *it == processor ) { + if (*it == m_SyncSource) { + debugOutput(DEBUG_LEVEL_VERBOSE, "unregistering sync source\n"); + m_SyncSource = NULL; + } + m_TransmitProcessors.erase(it); + return true; + } + } + } + + debugFatal("Processor (%p) not found!\n",processor); + return false; //not found +} + +bool StreamProcessorManager::setSyncSource(StreamProcessor *s) { + debugOutput( DEBUG_LEVEL_VERBOSE, "Setting sync source to (%p)\n", s); + m_SyncSource=s; + return true; +} + +bool StreamProcessorManager::prepare() { + + debugOutput( DEBUG_LEVEL_VERBOSE, "Preparing...\n"); + m_is_slave=false; + if(!getOption("slaveMode", m_is_slave)) { + debugWarning("Could not retrieve slaveMode parameter, defaulting to false\n"); + } + + m_shutdown_needed=false; + + // if no sync source is set, select one here + if(m_SyncSource == NULL) { + debugWarning("Sync Source is not set. Defaulting to first StreamProcessor.\n"); + } + + // FIXME: put into separate method + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) + { + if(m_SyncSource == NULL) { + debugWarning(" => Sync Source is %p.\n", *it); + m_SyncSource = *it; + } + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) + { + if(m_SyncSource == NULL) { + debugWarning(" => Sync Source is %p.\n", *it); + m_SyncSource = *it; + } + } + + // now do the actual preparation of the SP's + debugOutput( DEBUG_LEVEL_VERBOSE, "Prepare Receive processors...\n"); + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + + if(!(*it)->setOption("slaveMode", m_is_slave)) { + debugOutput(DEBUG_LEVEL_VERBOSE, " note: could not set slaveMode option for (%p)...\n",(*it)); + } + + if(!(*it)->prepare()) { + debugFatal( " could not prepare (%p)...\n",(*it)); + return false; + } + } + debugOutput( DEBUG_LEVEL_VERBOSE, "Prepare Transmit processors...\n"); + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + if(!(*it)->setOption("slaveMode", m_is_slave)) { + debugOutput(DEBUG_LEVEL_VERBOSE, " note: could not set slaveMode option for (%p)...\n",(*it)); + } + if(!(*it)->prepare()) { + debugFatal( " could not prepare (%p)...\n",(*it)); + return false; + } + } + + // if there are no stream processors registered, + // fail + if (m_ReceiveProcessors.size() + m_TransmitProcessors.size() == 0) { + debugFatal("No stream processors registered, can't do anything usefull\n"); + return false; + } + return true; +} + +bool +StreamProcessorManager::startDryRunning() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Putting StreamProcessor streams into dry-running state...\n"); + debugOutput( DEBUG_LEVEL_VERBOSE, " Schedule start dry-running...\n"); + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + if (!(*it)->isDryRunning()) { + if(!(*it)->scheduleStartDryRunning(-1)) { + debugError("Could not put SP %p into the dry-running state\n", *it); + return false; + } + } else { + debugOutput( DEBUG_LEVEL_VERBOSE, " SP %p already dry-running...\n", *it); + } + } + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + if (!(*it)->isDryRunning()) { + if(!(*it)->scheduleStartDryRunning(-1)) { + debugError("Could not put SP %p into the dry-running state\n", *it); + return false; + } + } else { + debugOutput( DEBUG_LEVEL_VERBOSE, " SP %p already dry-running...\n", *it); + } + } + debugOutput( DEBUG_LEVEL_VERBOSE, " Waiting for all SP's to be dry-running...\n"); + // wait for the syncsource to start running. + // that will block the waitForPeriod call until everyone has started (theoretically) + int cnt = STREAMPROCESSORMANAGER_CYCLES_FOR_DRYRUN; // by then it should have started + bool all_dry_running = false; + while (!all_dry_running && cnt) { + all_dry_running = true; + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + all_dry_running &= (*it)->isDryRunning(); + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + all_dry_running &= (*it)->isDryRunning(); + } + + SleepRelativeUsec(125); + cnt--; + } + if(cnt==0) { + debugOutput(DEBUG_LEVEL_VERBOSE, " Timeout waiting for the SP's to start dry-running\n"); + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + debugOutput( DEBUG_LEVEL_VERBOSE, " %s SP %p has state %s\n", + (*it)->getTypeString(), *it, (*it)->getStateString()); + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + debugOutput( DEBUG_LEVEL_VERBOSE, " %s SP %p has state %s\n", + (*it)->getTypeString(), *it, (*it)->getStateString()); + } + return false; + } + debugOutput( DEBUG_LEVEL_VERBOSE, " StreamProcessor streams dry-running...\n"); + return true; +} + +bool StreamProcessorManager::syncStartAll() { + if(m_SyncSource == NULL) return false; + // figure out when to get the SP's running. + // the xmit SP's should also know the base timestamp + // streams should be aligned here + + // now find out how long we have to delay the wait operation such that + // the received frames will all be presented to the SP + debugOutput( DEBUG_LEVEL_VERBOSE, "Finding minimal sync delay...\n"); + int max_of_min_delay = 0; + int min_delay = 0; + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + min_delay = (*it)->getMaxFrameLatency(); + if(min_delay > max_of_min_delay) max_of_min_delay = min_delay; + } + + // add some processing margin. This only shifts the time + // at which the buffer is transfer()'ed. This makes things somewhat + // more robust. It should be noted though that shifting the transfer + // time to a later time instant also causes the xmit buffer fill to be + // lower on average. + max_of_min_delay += STREAMPROCESSORMANAGER_SIGNAL_DELAY_TICKS; + debugOutput( DEBUG_LEVEL_VERBOSE, " sync delay = %d ticks (%03us %04uc %04ut)...\n", + max_of_min_delay, + (unsigned int)TICKS_TO_SECS(max_of_min_delay), + (unsigned int)TICKS_TO_CYCLES(max_of_min_delay), + (unsigned int)TICKS_TO_OFFSET(max_of_min_delay)); + m_SyncSource->setSyncDelay(max_of_min_delay); + + //STEP X: when we implement such a function, we can wait for a signal from the devices that they + // have aquired lock + //debugOutput( DEBUG_LEVEL_VERBOSE, "Waiting for device(s) to indicate clock sync lock...\n"); + //sleep(2); // FIXME: be smarter here + + // make sure that we are dry-running long enough for the + // DLL to have a decent sync (FIXME: does the DLL get updated when dry-running)? + debugOutput( DEBUG_LEVEL_VERBOSE, "Waiting for sync...\n"); + + unsigned int nb_sync_runs = (STREAMPROCESSORMANAGER_SYNC_WAIT_TIME_MSEC * getNominalRate()); + nb_sync_runs /= 1000; + nb_sync_runs /= getPeriodSize(); + + int64_t time_till_next_period; + while(nb_sync_runs--) { // or while not sync-ed? + // check if we were woken up too soon + time_till_next_period = m_SyncSource->getTimeUntilNextPeriodSignalUsecs(); + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "waiting for %d usecs...\n", time_till_next_period); + if(time_till_next_period > 0) { + // wait for the period + SleepRelativeUsec(time_till_next_period); + } + } + + debugOutput( DEBUG_LEVEL_VERBOSE, "Propagate sync info...\n"); + // FIXME: in the SPM it would be nice to have system time instead of + // 1394 time + + // we now should have decent sync info on the sync source + // determine a point in time where the system should start + // figure out where we are now + uint64_t time_of_first_sample = m_SyncSource->getTimeAtPeriod(); + debugOutput( DEBUG_LEVEL_VERBOSE, " sync at TS=%011llu (%03us %04uc %04ut)...\n", + time_of_first_sample, + (unsigned int)TICKS_TO_SECS(time_of_first_sample), + (unsigned int)TICKS_TO_CYCLES(time_of_first_sample), + (unsigned int)TICKS_TO_OFFSET(time_of_first_sample)); + + // start wet-running in STREAMPROCESSORMANAGER_CYCLES_FOR_STARTUP cycles + // this is the time window we have to setup all SP's such that they + // can start wet-running correctly. + time_of_first_sample = addTicks(time_of_first_sample, + STREAMPROCESSORMANAGER_CYCLES_FOR_STARTUP * TICKS_PER_CYCLE); + + debugOutput( DEBUG_LEVEL_VERBOSE, " => first sample at TS=%011llu (%03us %04uc %04ut)...\n", + time_of_first_sample, + (unsigned int)TICKS_TO_SECS(time_of_first_sample), + (unsigned int)TICKS_TO_CYCLES(time_of_first_sample), + (unsigned int)TICKS_TO_OFFSET(time_of_first_sample)); + + // we should start wet-running the transmit SP's some cycles in advance + // such that we know it is wet-running when it should output its first sample + uint64_t time_to_start_xmit = substractTicks(time_of_first_sample, + STREAMPROCESSORMANAGER_PRESTART_CYCLES_FOR_XMIT * TICKS_PER_CYCLE); + + uint64_t time_to_start_recv = substractTicks(time_of_first_sample, + STREAMPROCESSORMANAGER_PRESTART_CYCLES_FOR_RECV * TICKS_PER_CYCLE); + debugOutput( DEBUG_LEVEL_VERBOSE, " => xmit starts at TS=%011llu (%03us %04uc %04ut)...\n", + time_to_start_xmit, + (unsigned int)TICKS_TO_SECS(time_to_start_xmit), + (unsigned int)TICKS_TO_CYCLES(time_to_start_xmit), + (unsigned int)TICKS_TO_OFFSET(time_to_start_xmit)); + debugOutput( DEBUG_LEVEL_VERBOSE, " => recv starts at TS=%011llu (%03us %04uc %04ut)...\n", + time_to_start_recv, + (unsigned int)TICKS_TO_SECS(time_to_start_recv), + (unsigned int)TICKS_TO_CYCLES(time_to_start_recv), + (unsigned int)TICKS_TO_OFFSET(time_to_start_recv)); + + // at this point the buffer head timestamp of the transmit buffers can be set + // this is the presentation time of the first sample in the buffer + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + (*it)->setBufferHeadTimestamp(time_of_first_sample); + } + + // switch syncsource to running state + uint64_t time_to_start_sync; + // FIXME: this is most likely not going to work for transmit sync sources + // but those are unsupported in this version + if(m_SyncSource->getType() == StreamProcessor::ePT_Receive ) { + time_to_start_sync = time_to_start_recv; + } else { + time_to_start_sync = time_to_start_xmit; + } + if(!m_SyncSource->scheduleStartRunning(time_to_start_sync)) { + debugError("m_SyncSource->scheduleStartRunning(%11llu) failed\n", time_to_start_sync); + return false; + } + + // STEP X: switch all non-syncsource SP's over to the running state + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + if(*it != m_SyncSource) { + if(!(*it)->scheduleStartRunning(time_to_start_recv)) { + debugError("%p->scheduleStartRunning(%11llu) failed\n", *it, time_to_start_recv); + return false; + } + } + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + if(*it != m_SyncSource) { + if(!(*it)->scheduleStartRunning(time_to_start_xmit)) { + debugError("%p->scheduleStartRunning(%11llu) failed\n", *it, time_to_start_xmit); + return false; + } + } + } + // wait for the syncsource to start running. + // that will block the waitForPeriod call until everyone has started (theoretically) + // note: the SP's are scheduled to start in STREAMPROCESSORMANAGER_CYCLES_FOR_STARTUP cycles, + // so a 20 times this value should be a good timeout + int cnt = STREAMPROCESSORMANAGER_CYCLES_FOR_STARTUP * 20; // by then it should have started + while (!m_SyncSource->isRunning() && cnt) { + SleepRelativeUsec(125); + cnt--; + } + if(cnt==0) { + debugOutput(DEBUG_LEVEL_VERBOSE, " Timeout waiting for the SyncSource to get started\n"); + return false; + } + + // the sync source is running, we can now read a decent received timestamp from it + m_time_of_transfer = m_SyncSource->getTimeAtPeriod(); + + // and a (still very rough) approximation of the rate + float rate = m_SyncSource->getTicksPerFrame(); + int64_t delay_in_ticks=(int64_t)(((float)((m_nb_buffers-1) * m_period)) * rate); + debugOutput( DEBUG_LEVEL_VERBOSE, " initial time of transfer %010lld, rate %f...\n", + m_time_of_transfer, rate); + + // then use this information to initialize the xmit handlers + + // we now set the buffer tail timestamp of the transmit buffer + // to the period transfer time instant plus what's nb_buffers - 1 + // in ticks. This due to the fact that we (should) have received one period + // worth of ticks at t=m_time_of_transfer + // hence one period of frames should also have been transmitted, which means + // that there should be (nb_buffers - 1) * periodsize of frames in the xmit buffer + // that allows us to calculate the tail timestamp for the buffer. + + int64_t transmit_tail_timestamp = addTicks(m_time_of_transfer, delay_in_ticks); + + debugOutput( DEBUG_LEVEL_VERBOSE, " preset transmit tail TS %010lld, rate %f...\n", + transmit_tail_timestamp, rate); + + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + (*it)->setBufferTailTimestamp(transmit_tail_timestamp); + (*it)->setTicksPerFrame(rate); + } + + // align the received streams to be phase aligned + if(!alignReceivedStreams()) { + debugError("Could not align streams...\n"); + return false; + } + + debugOutput( DEBUG_LEVEL_VERBOSE, " StreamProcessor streams running...\n"); + return true; +} + +bool +StreamProcessorManager::alignReceivedStreams() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Aligning received streams...\n"); + unsigned int nb_sync_runs; + unsigned int nb_rcv_sp = m_ReceiveProcessors.size(); + int64_t diff_between_streams[nb_rcv_sp]; + int64_t diff; + + unsigned int i; + + unsigned int periods_per_align_try = (STREAMPROCESSORMANAGER_ALIGN_AVERAGE_TIME_MSEC * getNominalRate()); + periods_per_align_try /= 1000; + periods_per_align_try /= getPeriodSize(); + debugOutput( DEBUG_LEVEL_VERBOSE, " averaging over %u periods...\n", periods_per_align_try); + + bool aligned = false; + int cnt = STREAMPROCESSORMANAGER_NB_ALIGN_TRIES; + while (!aligned && cnt--) { + nb_sync_runs = periods_per_align_try; + while(nb_sync_runs) { + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " check (%d)...\n", nb_sync_runs); + if(!waitForPeriod()) { + debugWarning("xrun while aligning streams...\n"); + return false; + } + + // before we do anything else, transfer + if(!transferSilence()) { + debugError("Could not transfer silence\n"); + return false; + } + + // now calculate the stream offset + i = 0; + for ( i = 0; i < nb_rcv_sp; i++) { + StreamProcessor *s = m_ReceiveProcessors.at(i); + diff = diffTicks(m_SyncSource->getTimeAtPeriod(), s->getTimeAtPeriod()); + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " offset between SyncSP %p and SP %p is %lld ticks...\n", + m_SyncSource, s, diff); + if ( nb_sync_runs == periods_per_align_try ) { + diff_between_streams[i] = diff; + } else { + diff_between_streams[i] += diff; + } + } + + nb_sync_runs--; + } + // calculate the average offsets + debugOutput( DEBUG_LEVEL_VERBOSE, " Average offsets:\n"); + int diff_between_streams_frames[nb_rcv_sp]; + aligned = true; + for ( i = 0; i < nb_rcv_sp; i++) { + StreamProcessor *s = m_ReceiveProcessors.at(i); + + diff_between_streams[i] /= periods_per_align_try; + diff_between_streams_frames[i] = (int)roundf(diff_between_streams[i] / s->getTicksPerFrame()); + debugOutput( DEBUG_LEVEL_VERBOSE, " avg offset between SyncSP %p and SP %p is %lld ticks, %d frames...\n", + m_SyncSource, s, diff_between_streams[i], diff_between_streams_frames[i]); + + aligned &= (diff_between_streams_frames[i] == 0); + + // reposition the stream + if(!s->shiftStream(diff_between_streams_frames[i])) { + debugError("Could not shift SP %p %d frames\n", s, diff_between_streams_frames[i]); + return false; + } + } + if (!aligned) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Streams not aligned, doing new round...\n"); + } + } + if (cnt == 0) { + debugError("Align failed\n"); + return false; + } + return true; +} + +bool StreamProcessorManager::start() { + debugOutput( DEBUG_LEVEL_VERBOSE, "Starting Processors...\n"); + + // start all SP's synchonized + bool start_result = false; + for (int ntries=0; ntries < STREAMPROCESSORMANAGER_SYNCSTART_TRIES; ntries++) { + // put all SP's into dry-running state + if (!startDryRunning()) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Could not put SP's in dry-running state (try %d)\n", ntries); + start_result = false; + continue; + } + + start_result = syncStartAll(); + if(start_result) { + break; + } else { + debugOutput(DEBUG_LEVEL_VERBOSE, "Sync start try %d failed...\n", ntries); + } + } + if (!start_result) { + debugFatal("Could not syncStartAll...\n"); + return false; + } + + return true; +} + +bool StreamProcessorManager::stop() { + debugOutput( DEBUG_LEVEL_VERBOSE, "Stopping...\n"); + + debugOutput( DEBUG_LEVEL_VERBOSE, " scheduling stop for all SP's...\n"); + // switch SP's over to the dry-running state + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + if((*it)->isRunning()) { + if(!(*it)->scheduleStopRunning(-1)) { + debugError("%p->scheduleStopRunning(-1) failed\n", *it); + return false; + } + } + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + if((*it)->isRunning()) { + if(!(*it)->scheduleStopRunning(-1)) { + debugError("%p->scheduleStopRunning(-1) failed\n", *it); + return false; + } + } + } + // wait for the SP's to get into the dry-running/stopped state + int cnt = 8000; + bool ready = false; + while (!ready && cnt) { + ready = true; + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + ready &= ((*it)->isDryRunning() || (*it)->isStopped() || (*it)->isWaitingForStream()); + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + ready &= ((*it)->isDryRunning() || (*it)->isStopped() || (*it)->isWaitingForStream()); + } + SleepRelativeUsec(125); + cnt--; + } + if(cnt==0) { + debugWarning(" Timeout waiting for the SP's to start dry-running\n"); + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + (*it)->dumpInfo(); + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + (*it)->dumpInfo(); + } + return false; + } + + // switch SP's over to the stopped state + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + if(!(*it)->scheduleStopDryRunning(-1)) { + debugError("%p->scheduleStopDryRunning(-1) failed\n", *it); + return false; + } + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + if(!(*it)->scheduleStopDryRunning(-1)) { + debugError("%p->scheduleStopDryRunning(-1) failed\n", *it); + return false; + } + } + // wait for the SP's to get into the stopped state + cnt = 8000; + ready = false; + while (!ready && cnt) { + ready = true; + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + ready &= (*it)->isStopped(); + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + ready &= (*it)->isStopped(); + } + SleepRelativeUsec(125); + cnt--; + } + if(cnt==0) { + debugWarning(" Timeout waiting for the SP's to stop\n"); + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + (*it)->dumpInfo(); + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + (*it)->dumpInfo(); + } + return false; + } + return true; +} + +/** + * Called upon Xrun events. This brings all StreamProcessors back + * into their starting state, and then carries on streaming. This should + * have the same effect as restarting the whole thing. + * + * @return true if successful, false otherwise + */ +bool StreamProcessorManager::handleXrun() { + + debugOutput( DEBUG_LEVEL_VERBOSE, "Handling Xrun ...\n"); + + dumpInfo(); + + /* + * Reset means: + * 1) Disabling the SP's, so that they don't process any packets + * note: the isomanager does keep on delivering/requesting them + * 2) Bringing all buffers & streamprocessors into a know state + * - Clear all capture buffers + * - Put nb_periods*period_size of null frames into the playback buffers + * 3) Re-enable the SP's + */ + + debugOutput( DEBUG_LEVEL_VERBOSE, "Restarting StreamProcessors...\n"); + // start all SP's synchonized + bool start_result = false; + for (int ntries=0; ntries < STREAMPROCESSORMANAGER_SYNCSTART_TRIES; ntries++) { + if(m_shutdown_needed) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Shutdown requested...\n"); + return true; + } + // put all SP's into dry-running state + if (!startDryRunning()) { + debugShowBackLog(); + debugOutput(DEBUG_LEVEL_VERBOSE, "Could not put SP's in dry-running state (try %d)\n", ntries); + start_result = false; + continue; + } + + start_result = syncStartAll(); + if(start_result) { + break; + } else { + debugOutput(DEBUG_LEVEL_VERBOSE, "Sync start try %d failed...\n", ntries); + } + } + if (!start_result) { + debugFatal("Could not syncStartAll...\n"); + return false; + } + debugOutput( DEBUG_LEVEL_VERBOSE, "Xrun handled...\n"); + + return true; +} + +/** + * @brief Waits until the next period of samples is ready + * + * This function does not return until a full period of samples is (or should be) + * ready to be transferred. + * + * @return true if the period is ready, false if not + */ +bool StreamProcessorManager::waitForPeriod() { + if(m_SyncSource == NULL) return false; + if(m_shutdown_needed) return false; + bool xrun_occurred = false; + bool period_not_ready = true; + + // grab the wait lock + // this ensures that bus reset handling doesn't interfere + Util::MutexLockHelper lock(*m_WaitLock); + + while(period_not_ready) { + debugOutputExtreme(DEBUG_LEVEL_VERBOSE, + "waiting for period (%d frames in buffer)...\n", + m_SyncSource->getBufferFill()); + + // wait for something to happen + switch(waitForActivity()) { + case eAR_Error: + debugError("Error while waiting for activity\n"); + return false; + case eAR_Interrupted: + // FIXME: what to do here? + debugWarning("Interrupted while waiting for activity\n"); + break; + case eAR_Timeout: + // FIXME: what to do here? + debugWarning("Timeout while waiting for activity\n"); + break; + case eAR_Activity: + // do nothing + break; + } + debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "got activity...\n"); + + // HACK: this should be solved more elegantly + period_not_ready = false; + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + bool this_sp_period_ready = (*it)->canConsumePeriod(); + if (!this_sp_period_ready) { + period_not_ready = true; + } + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + bool this_sp_period_ready = (*it)->canProducePeriod(); + if (!this_sp_period_ready) { + period_not_ready = true; + } + } + debugOutputExtreme(DEBUG_LEVEL_VERBOSE, " period not ready? %d...\n", period_not_ready); + + // check for underruns on the ISO side, + // those should make us bail out of the wait loop + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + // a xrun has occurred on the Iso side + xrun_occurred |= (*it)->xrunOccurred(); + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + // a xrun has occurred on the Iso side + xrun_occurred |= (*it)->xrunOccurred(); + } + if(xrun_occurred) break; + // FIXME: make sure we also exit this loop when something else happens (e.g. signal, iso error) + + // if we have to shutdown due to some async event (busreset), do so + if(m_shutdown_needed) break; + } + + if(xrun_occurred) { + debugOutput( DEBUG_LEVEL_VERBOSE, "exit due to xrun...\n"); + } + + // we save the 'ideal' time of the transfer at this point, + // because we can have interleaved read - process - write + // cycles making that we modify a receiving stream's buffer + // before we get to writing. + // NOTE: before waitForPeriod() is called again, both the transmit + // and the receive processors should have done their transfer. + m_time_of_transfer = m_SyncSource->getTimeAtPeriod(); + + #ifdef DEBUG + static uint64_t m_time_of_transfer2 = m_time_of_transfer; + + int ticks_per_period = (int)(m_SyncSource->getTicksPerFrame() * m_period); + int diff=diffTicks(m_time_of_transfer, m_time_of_transfer2); + // display message if the difference between two successive tick + // values is more than 50 ticks. 1 sample at 48k is 512 ticks + // so 50 ticks = 10%, which is a rather large jitter value. + if(diff-ticks_per_period > 50 || diff-ticks_per_period < -50) { + debugOutput(DEBUG_LEVEL_VERBOSE, "rather large TSP difference TS=%011llu => TS=%011llu (%d, nom %d)\n", + m_time_of_transfer2, m_time_of_transfer, diff, ticks_per_period); + } + m_time_of_transfer2 = m_time_of_transfer; + #endif + + debugOutputExtreme( DEBUG_LEVEL_VERBOSE, + "transfer period %d at %llu ticks...\n", + m_nbperiods, m_time_of_transfer); + + // this is to notify the client of the delay that we introduced by waiting + m_delayed_usecs = - m_SyncSource->getTimeUntilNextPeriodSignalUsecs(); + debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, + "delayed for %d usecs...\n", + m_delayed_usecs); + +#ifdef DEBUG + int rcv_bf=0, xmt_bf=0; + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + rcv_bf = (*it)->getBufferFill(); + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + xmt_bf = (*it)->getBufferFill(); + } + debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, + "XF at %011llu ticks, RBF=%d, XBF=%d, SUM=%d...\n", + m_time_of_transfer, rcv_bf, xmt_bf, rcv_bf+xmt_bf); + + // check if xruns occurred on the Iso side. + // also check if xruns will occur should we transfer() now + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + + if ((*it)->xrunOccurred()) { + debugOutput(DEBUG_LEVEL_NORMAL, + "Xrun on RECV SP %p due to ISO side xrun\n", *it); + (*it)->dumpInfo(); + } + if (!((*it)->canClientTransferFrames(m_period))) { + debugOutput(DEBUG_LEVEL_NORMAL, + "Xrun on RECV SP %p due to buffer side xrun\n", *it); + (*it)->dumpInfo(); + } + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + if ((*it)->xrunOccurred()) { + debugOutput(DEBUG_LEVEL_NORMAL, + "Xrun on XMIT SP %p due to ISO side xrun\n", *it); + } + if (!((*it)->canClientTransferFrames(m_period))) { + debugOutput(DEBUG_LEVEL_NORMAL, + "Xrun on XMIT SP %p due to buffer side xrun\n", *it); + } + } +#endif + m_nbperiods++; + // now we can signal the client that we are (should be) ready + return !xrun_occurred; +} + +/** + * @brief Transfer one period of frames for both receive and transmit StreamProcessors + * + * Transfers one period of frames from the client side to the Iso side and vice versa. + * + * @return true if successful, false otherwise (indicates xrun). + */ +bool StreamProcessorManager::transfer() { + debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, "Transferring period...\n"); + bool retval=true; + retval &= transfer(StreamProcessor::ePT_Receive); + retval &= transfer(StreamProcessor::ePT_Transmit); + return retval; +} + +/** + * @brief Transfer one period of frames for either the receive or transmit StreamProcessors + * + * Transfers one period of frames from the client side to the Iso side or vice versa. + * + * @param t The processor type to tranfer for (receive or transmit) + * @return true if successful, false otherwise (indicates xrun). + */ +bool StreamProcessorManager::transfer(enum StreamProcessor::eProcessorType t) { + if(m_SyncSource == NULL) return false; + debugOutputExtreme( DEBUG_LEVEL_VERY_VERBOSE, + "transfer(%d) at TS=%011llu (%03us %04uc %04ut)...\n", + t, m_time_of_transfer, + (unsigned int)TICKS_TO_SECS(m_time_of_transfer), + (unsigned int)TICKS_TO_CYCLES(m_time_of_transfer), + (unsigned int)TICKS_TO_OFFSET(m_time_of_transfer)); + + bool retval = true; + // a static cast could make sure that there is no performance + // penalty for the virtual functions (to be checked) + if (t==StreamProcessor::ePT_Receive) { + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + if(!(*it)->getFrames(m_period, m_time_of_transfer)) { + debugWarning("could not getFrames(%u, %11llu) from stream processor (%p)\n", + m_period, m_time_of_transfer,*it); + retval &= false; // buffer underrun + } + } + } else { + // FIXME: in the SPM it would be nice to have system time instead of + // 1394 time + float rate = m_SyncSource->getTicksPerFrame(); + int64_t one_ringbuffer_in_ticks=(int64_t)(((float)((m_nb_buffers * m_period))) * rate); + + // the data we are putting into the buffer is intended to be transmitted + // one ringbuffer size after it has been received + int64_t transmit_timestamp = addTicks(m_time_of_transfer, one_ringbuffer_in_ticks); + + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + // FIXME: in the SPM it would be nice to have system time instead of + // 1394 time + if(!(*it)->putFrames(m_period, transmit_timestamp)) { + debugWarning("could not putFrames(%u,%llu) to stream processor (%p)\n", + m_period, transmit_timestamp, *it); + retval &= false; // buffer underrun + } + } + } + return retval; +} + +/** + * @brief Transfer one period of silence for both receive and transmit StreamProcessors + * + * Transfers one period of silence to the Iso side for transmit SP's + * or dump one period of frames for receive SP's + * + * @return true if successful, false otherwise (indicates xrun). + */ +bool StreamProcessorManager::transferSilence() { + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Transferring silent period...\n"); + bool retval=true; + // NOTE: the order here is opposite from the order in + // normal operation (transmit is before receive), because + // we can do that here (data=silence=available) and + // it increases reliability (esp. on startup) + retval &= transferSilence(StreamProcessor::ePT_Transmit); + retval &= transferSilence(StreamProcessor::ePT_Receive); + return retval; +} + +/** + * @brief Transfer one period of silence for either the receive or transmit StreamProcessors + * + * Transfers one period of silence to the Iso side for transmit SP's + * or dump one period of frames for receive SP's + * + * @param t The processor type to tranfer for (receive or transmit) + * @return true if successful, false otherwise (indicates xrun). + */ +bool StreamProcessorManager::transferSilence(enum StreamProcessor::eProcessorType t) { + if(m_SyncSource == NULL) return false; + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, + "transferSilence(%d) at TS=%011llu (%03us %04uc %04ut)...\n", + t, m_time_of_transfer, + (unsigned int)TICKS_TO_SECS(m_time_of_transfer), + (unsigned int)TICKS_TO_CYCLES(m_time_of_transfer), + (unsigned int)TICKS_TO_OFFSET(m_time_of_transfer)); + + bool retval = true; + // a static cast could make sure that there is no performance + // penalty for the virtual functions (to be checked) + if (t==StreamProcessor::ePT_Receive) { + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + if(!(*it)->dropFrames(m_period, m_time_of_transfer)) { + debugWarning("could not dropFrames(%u, %11llu) from stream processor (%p)\n", + m_period, m_time_of_transfer,*it); + retval &= false; // buffer underrun + } + } + } else { + // FIXME: in the SPM it would be nice to have system time instead of + // 1394 time + float rate = m_SyncSource->getTicksPerFrame(); + int64_t one_ringbuffer_in_ticks=(int64_t)(((float)(m_nb_buffers * m_period)) * rate); + + // the data we are putting into the buffer is intended to be transmitted + // one ringbuffer size after it has been received + int64_t transmit_timestamp = addTicks(m_time_of_transfer, one_ringbuffer_in_ticks); + + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + // FIXME: in the SPM it would be nice to have system time instead of + // 1394 time + if(!(*it)->putSilenceFrames(m_period, transmit_timestamp)) { + debugWarning("could not putSilenceFrames(%u,%llu) to stream processor (%p)\n", + m_period, transmit_timestamp, *it); + retval &= false; // buffer underrun + } + } + } + return retval; +} + +void StreamProcessorManager::dumpInfo() { + debugOutputShort( DEBUG_LEVEL_NORMAL, "----------------------------------------------------\n"); + debugOutputShort( DEBUG_LEVEL_NORMAL, "Dumping StreamProcessorManager information...\n"); + debugOutputShort( DEBUG_LEVEL_NORMAL, "Period count: %6d\n", m_nbperiods); + debugOutputShort( DEBUG_LEVEL_NORMAL, "Data type: %s\n", (m_audio_datatype==eADT_Float?"float":"int24")); + + debugOutputShort( DEBUG_LEVEL_NORMAL, " Receive processors...\n"); + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + (*it)->dumpInfo(); + } + + debugOutputShort( DEBUG_LEVEL_NORMAL, " Transmit processors...\n"); + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + (*it)->dumpInfo(); + } + + debugOutputShort( DEBUG_LEVEL_NORMAL, "----------------------------------------------------\n"); + +} + +void StreamProcessorManager::setVerboseLevel(int l) { + setDebugLevel(l); + if(m_WaitLock) m_WaitLock->setVerboseLevel(l); + + debugOutput( DEBUG_LEVEL_VERBOSE, " Receive processors...\n"); + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + (*it)->setVerboseLevel(l); + } + + debugOutput( DEBUG_LEVEL_VERBOSE, " Transmit processors...\n"); + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + (*it)->setVerboseLevel(l); + } +} + + +int StreamProcessorManager::getPortCount(enum Port::E_PortType type, enum Port::E_Direction direction) { + int count=0; + + if (direction == Port::E_Capture) { + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + count += (*it)->getPortCount(type); + } + } else { + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + count += (*it)->getPortCount(type); + } + } + return count; +} + +int StreamProcessorManager::getPortCount(enum Port::E_Direction direction) { + int count=0; + + if (direction == Port::E_Capture) { + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + count += (*it)->getPortCount(); + } + } else { + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + count += (*it)->getPortCount(); + } + } + return count; +} + +// TODO: implement a port map here, instead of the loop + +Port* StreamProcessorManager::getPortByIndex(int idx, enum Port::E_Direction direction) { + int count=0; + int prevcount=0; + + if (direction == Port::E_Capture) { + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + count += (*it)->getPortCount(); + if (count > idx) { + return (*it)->getPortAtIdx(idx-prevcount); + } + prevcount=count; + } + } else { + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + count += (*it)->getPortCount(); + if (count > idx) { + return (*it)->getPortAtIdx(idx-prevcount); + } + prevcount=count; + } + } + return NULL; +} + +bool StreamProcessorManager::setThreadParameters(bool rt, int priority) { + m_thread_realtime=rt; + m_thread_priority=priority; + return true; +} + + +} // end of namespace Index: /branches/libffado-2.0/src/libstreaming/StreamProcessorManager.h =================================================================== --- /branches/libffado-2.0/src/libstreaming/StreamProcessorManager.h (revision 1045) +++ /branches/libffado-2.0/src/libstreaming/StreamProcessorManager.h (revision 1045) @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_STREAMPROCESSORMANAGER__ +#define __FFADO_STREAMPROCESSORMANAGER__ + +#include "generic/Port.h" +#include "generic/StreamProcessor.h" + +#include "debugmodule/debugmodule.h" +#include "libutil/Thread.h" +#include "libutil/Mutex.h" +#include "libutil/OptionContainer.h" + +#include +#include + +namespace Streaming { + +class StreamProcessor; + +typedef std::vector StreamProcessorVector; +typedef std::vector::iterator StreamProcessorVectorIterator; + +/*! +\brief Manages a collection of StreamProcessors and provides a synchronisation interface + +*/ +class StreamProcessorManager : public Util::OptionContainer { + friend class StreamProcessor; + +public: + enum eADT_AudioDataType { + eADT_Int24, + eADT_Float, + }; + + StreamProcessorManager(); + StreamProcessorManager(unsigned int period, unsigned int rate, unsigned int nb_buffers); + virtual ~StreamProcessorManager(); + + void handleBusReset(); + + bool prepare(); ///< to be called after the processors are registered + + bool start(); + bool stop(); + + bool startDryRunning(); + bool syncStartAll(); + // activity signaling + enum eActivityResult { + eAR_Activity, + eAR_Timeout, + eAR_Interrupted, + eAR_Error + }; + void signalActivity(); + enum eActivityResult waitForActivity(); + + // this is the setup API + bool registerProcessor(StreamProcessor *processor); ///< start managing a streamprocessor + bool unregisterProcessor(StreamProcessor *processor); ///< stop managing a streamprocessor + + void setPeriodSize(unsigned int period) + {m_period = period;}; + unsigned int getPeriodSize() + {return m_period;}; + + bool setAudioDataType(enum eADT_AudioDataType t) + {m_audio_datatype = t; return true;}; + enum eADT_AudioDataType getAudioDataType() + {return m_audio_datatype;} + + void setNbBuffers(unsigned int nb_buffers) + {m_nb_buffers = nb_buffers;}; + int getNbBuffers() + {return m_nb_buffers;}; + + int getPortCount(enum Port::E_PortType, enum Port::E_Direction); + int getPortCount(enum Port::E_Direction); + Port* getPortByIndex(int idx, enum Port::E_Direction); + + // the client-side functions + bool waitForPeriod(); + bool transfer(); + bool transfer(enum StreamProcessor::eProcessorType); +private: + bool transferSilence(); + bool transferSilence(enum StreamProcessor::eProcessorType); + + bool alignReceivedStreams(); +public: + int getDelayedUsecs() {return m_delayed_usecs;}; + bool xrunOccurred(); + bool shutdownNeeded() {return m_shutdown_needed;}; + int getXrunCount() {return m_xruns;}; + + void setNominalRate(unsigned int r) {m_nominal_framerate = r;}; + unsigned int getNominalRate() {return m_nominal_framerate;}; + uint64_t getTimeOfLastTransfer() { return m_time_of_transfer;}; + +private: + int m_delayed_usecs; + // this stores the time at which the next transfer should occur + // usually this is in the past, but it is needed as a timestamp + // for the transmit SP's + uint64_t m_time_of_transfer; + +public: + bool handleXrun(); ///< reset the streams & buffers after xrun + + bool setThreadParameters(bool rt, int priority); + + virtual void setVerboseLevel(int l); + void dumpInfo(); + +private: // slaving support + bool m_is_slave; + + // the sync source stuff +private: + StreamProcessor *m_SyncSource; + +public: + bool setSyncSource(StreamProcessor *s); + StreamProcessor& getSyncSource() + {return *m_SyncSource;}; + +protected: // FIXME: private? + + // thread related vars + bool m_xrun_happened; + int m_activity_wait_timeout_usec; + bool m_thread_realtime; + int m_thread_priority; + + // activity signaling + sem_t m_activity_semaphore; + + // processor list + StreamProcessorVector m_ReceiveProcessors; + StreamProcessorVector m_TransmitProcessors; + + unsigned int m_nb_buffers; + unsigned int m_period; + enum eADT_AudioDataType m_audio_datatype; + unsigned int m_nominal_framerate; + unsigned int m_xruns; + bool m_shutdown_needed; + + unsigned int m_nbperiods; + + Util::Mutex *m_WaitLock; + + DECLARE_DEBUG_MODULE; + +}; + +} + +#endif /* __FFADO_STREAMPROCESSORMANAGER__ */ + + Index: /branches/libffado-2.0/src/libstreaming/generic/PortManager.cpp =================================================================== --- /branches/libffado-2.0/src/libstreaming/generic/PortManager.cpp (revision 876) +++ /branches/libffado-2.0/src/libstreaming/generic/PortManager.cpp (revision 876) @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "PortManager.h" +#include "Port.h" + +#include + +#include +#include + + +namespace Streaming { + +IMPL_DEBUG_MODULE( PortManager, PortManager, DEBUG_LEVEL_NORMAL ); + +PortManager::PortManager() { +} + +PortManager::~PortManager() { + flushDebugOutput(); + // delete all ports that are still registered to the manager + while (m_Ports.size()) { + // This will also remove the port from m_Ports via + // PortManager::unregister(). + delete m_Ports.front(); + } +} + +bool PortManager::makeNameUnique(Port *port) +{ + bool done=false; + int idx=0; + std::string portname_orig=port->getName(); + + while(!done && idx<10000) { + bool is_unique=true; + + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + is_unique &= !((*it)->getName() == port->getName()); + } + + if (is_unique) { + done=true; + } else { + std::ostringstream portname; + portname << portname_orig << idx++; + + port->setName(portname.str()); + } + } + + if(idx<10000) return true; + else return false; +} + +/** + * + * @param port + * @return + */ +bool PortManager::registerPort(Port *port) +{ + assert(port); + + debugOutput( DEBUG_LEVEL_VERBOSE, "Adding port %s, type: %d, dir: %d\n", + port->getName().c_str(), port->getPortType(), port->getDirection()); + + port->setVerboseLevel(getDebugLevel()); + + if (makeNameUnique(port)) { + m_Ports.push_back(port); + return true; + } else { + return false; + } +} + +bool PortManager::unregisterPort(Port *port) +{ + assert(port); + debugOutput( DEBUG_LEVEL_VERBOSE, "unregistering port %s\n",port->getName().c_str()); + + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + if(*it == port) { + m_Ports.erase(it); + return true; + } + } + + debugOutput( DEBUG_LEVEL_VERBOSE, "port %s not found \n",port->getName().c_str()); + + return false; //not found + +} + +int PortManager::getPortCount(enum Port::E_PortType type) { + int count=0; + + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + if ( (*it)->getPortType() == type ) { + count++; + } + } + return count; +} + +int PortManager::getPortCount() { + int count=0; + + count+=m_Ports.size(); + + return count; +} + +Port * PortManager::getPortAtIdx(unsigned int index) { + + return m_Ports.at(index); + +} + +void PortManager::setVerboseLevel(int i) { + setDebugLevel(i); + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + (*it)->setVerboseLevel(i); + } +} + + +bool PortManager::resetPorts() { + debugOutput( DEBUG_LEVEL_VERBOSE, "reset ports\n"); + + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + if(!(*it)->reset()) { + debugFatal("Could not reset port %s",(*it)->getName().c_str()); + return false; + } + } + return true; +} + +bool PortManager::initPorts() { + debugOutput( DEBUG_LEVEL_VERBOSE, "init ports\n"); + + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + if(!(*it)->init()) { + debugFatal("Could not init port %s\n", (*it)->getName().c_str()); + return false; + } + } + return true; +} + +bool PortManager::preparePorts() { + debugOutput( DEBUG_LEVEL_VERBOSE, "preparing ports\n"); + + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + if(!(*it)->prepare()) { + debugFatal("Could not prepare port %s",(*it)->getName().c_str()); + return false; + } + + } + return true; +} + +} Index: /branches/libffado-2.0/src/libstreaming/generic/StreamProcessor.h =================================================================== --- /branches/libffado-2.0/src/libstreaming/generic/StreamProcessor.h (revision 1036) +++ /branches/libffado-2.0/src/libstreaming/generic/StreamProcessor.h (revision 1036) @@ -0,0 +1,466 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_STREAMPROCESSOR__ +#define __FFADO_STREAMPROCESSOR__ + +#include "ffadodevice.h" + +#include "PortManager.h" + +#include "libutil/StreamStatistics.h" +#include "libutil/TimestampedBuffer.h" +#include "libutil/OptionContainer.h" + +#include "debugmodule/debugmodule.h" + +#include + +class Ieee1394Service; +class IsoHandlerManager; + +namespace Streaming { + + class StreamProcessorManager; +/*! +\brief Class providing a generic interface for Stream Processors + + A stream processor multiplexes or demultiplexes an ISO stream into a + collection of ports. This class should be subclassed, and the relevant + functions should be overloaded. + +*/ +class StreamProcessor : public PortManager, + public Util::TimestampedBufferClient, + public Util::OptionContainer +{ +public: + ///> the streamprocessor type + enum eProcessorType { + ePT_Receive, + ePT_Transmit + }; + ///> returns the type of the streamprocessor + virtual enum eProcessorType getType() { return m_processor_type; }; +private: + // this can only be set by the constructor + enum eProcessorType m_processor_type; + // pretty printing + const char *ePTToString(enum eProcessorType); +protected: + ///> the state the streamprocessor is in + enum eProcessorState { + ePS_Invalid, + ePS_Created, + ePS_Stopped, + ePS_WaitingForStream, + ePS_DryRunning, + ePS_WaitingForStreamEnable, + ePS_Running, + ePS_WaitingForStreamDisable, + }; + + ///> set the SP state to a specific value + void setState(enum eProcessorState); + ///> get the SP state + enum eProcessorState getState() {return m_state;}; +private: + enum eProcessorState m_state; + // state switching + enum eProcessorState m_next_state; + unsigned int m_cycle_to_switch_state; + bool updateState(); + // pretty printing + const char *ePSToString(enum eProcessorState); + + bool doStop(); + bool doWaitForRunningStream(); + bool doDryRunning(); + bool doWaitForStreamEnable(); + bool doRunning(); + bool doWaitForStreamDisable(); + + bool scheduleStateTransition(enum eProcessorState state, uint64_t time_instant); + bool waitForState(enum eProcessorState state, unsigned int timeout); + +public: //--- state stuff + bool isRunning() + {return m_state == ePS_Running;}; + bool isDryRunning() + {return m_state == ePS_DryRunning;}; + bool isStopped() + {return m_state == ePS_Stopped;}; + bool isWaitingForStream() + {return m_state == ePS_WaitingForStream;}; + + // these schedule and wait for the state transition + bool startDryRunning(int64_t time_to_start_at); + bool startRunning(int64_t time_to_start_at); + bool stopDryRunning(int64_t time_to_stop_at); + bool stopRunning(int64_t time_to_stop_at); + + // these only schedule the transition + bool scheduleStartDryRunning(int64_t time_to_start_at); + bool scheduleStartRunning(int64_t time_to_start_at); + bool scheduleStopDryRunning(int64_t time_to_stop_at); + bool scheduleStopRunning(int64_t time_to_stop_at); + + // the main difference between init and prepare is that when prepare is called, + // the SP is registered to a manager (FIXME: can't it be called by the manager?) + bool init(); + bool prepare(); + + void handleBusReset(); + +public: // constructor/destructor + StreamProcessor(FFADODevice &parent, enum eProcessorType type); + virtual ~StreamProcessor(); +protected: + FFADODevice& m_Parent; + Ieee1394Service& m_1394service; + IsoHandlerManager& m_IsoHandlerManager; + StreamProcessorManager& m_StreamProcessorManager; + unsigned int m_local_node_id; + +public: // the public receive/transmit functions + // the transmit interface accepts frames and provides packets + // implement these for a transmit SP + // leave default for a receive SP + + // the receive interface accepts packets and provides frames + // these are implemented by the parent SP + enum raw1394_iso_disposition + putPacket(unsigned char *data, unsigned int length, + unsigned char channel, unsigned char tag, unsigned char sy, + uint32_t pkt_ctr, unsigned int dropped, unsigned int skipped); + + enum raw1394_iso_disposition + getPacket(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr, unsigned int dropped, + unsigned int skipped, unsigned int max_length); + + bool getFrames(unsigned int nbframes, int64_t ts); ///< transfer the buffer contents to the client + bool putFrames(unsigned int nbframes, int64_t ts); ///< transfer the client contents to the buffer + + bool canProducePacket(); + bool canProducePeriod(); + bool canProduce(unsigned int nframes); + + bool canConsumePacket(); + bool canConsumePeriod(); + bool canConsume(unsigned int nframes); + +public: + /** + * @brief drop nframes from the internal buffer as if they were transferred to the client side + * + * Gets nframes of frames from the buffer as done by getFrames(), but does not transfer them + * to the client side. Instead they are discarded. + * + * @param nframes number of frames + * @return true if the operation was successful + */ + bool dropFrames(unsigned int nframes, int64_t ts); + + /** + * @brief put silence frames into the internal buffer + * + * Puts nframes of frames into the buffer as done by putFrames(), but does not transfer them + * from the client side. Instead, silent frames are used. + * + * @param nframes number of frames + * @return true if the operation was successful + */ + bool putSilenceFrames(unsigned int nbframes, int64_t ts); + + /** + * @brief Shifts the stream with the specified number of frames + * + * Used to align several streams to each other. It comes down to + * making sure the head timestamp corresponds to the timestamp of + * one master stream + * + * @param nframes the number of frames to shift + * @return true if successful + */ + bool shiftStream(int nframes); + + /** + * @brief tries to fill/sink the stream as far as possible + */ + void flush(); + +protected: // the helper receive/transmit functions + enum eChildReturnValue { + eCRV_OK, + eCRV_Invalid, + eCRV_Packet, + eCRV_EmptyPacket, + eCRV_XRun, + eCRV_Again, + eCRV_Defer, + }; + // to be implemented by the children + // the following methods are to be implemented by receive SP subclasses + virtual enum eChildReturnValue processPacketHeader(unsigned char *data, unsigned int length, + unsigned char tag, unsigned char sy, + uint32_t pkt_ctr) + {debugWarning("call not allowed\n"); return eCRV_Invalid;}; + virtual enum eChildReturnValue processPacketData(unsigned char *data, unsigned int length) + {debugWarning("call not allowed\n"); return eCRV_Invalid;}; + virtual bool processReadBlock(char *data, unsigned int nevents, unsigned int offset) + {debugWarning("call not allowed\n"); return false;}; + + // the following methods are to be implemented by transmit SP subclasses + virtual enum eChildReturnValue generatePacketHeader(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr) + {debugWarning("call not allowed\n"); return eCRV_Invalid;}; + virtual enum eChildReturnValue generatePacketData(unsigned char *data, unsigned int *length) + {debugWarning("call not allowed\n"); return eCRV_Invalid;}; + virtual enum eChildReturnValue generateEmptyPacketHeader(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr) + {debugWarning("call not allowed\n"); return eCRV_Invalid;}; + virtual enum eChildReturnValue generateEmptyPacketData(unsigned char *data, unsigned int *length) + {debugWarning("call not allowed\n"); return eCRV_Invalid;}; + virtual enum eChildReturnValue generateSilentPacketHeader(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr) + {debugWarning("call not allowed\n"); return eCRV_Invalid;}; + virtual enum eChildReturnValue generateSilentPacketData(unsigned char *data, unsigned int *length) + {debugWarning("call not allowed\n"); return eCRV_Invalid;}; + virtual bool processWriteBlock(char *data, unsigned int nevents, unsigned int offset) + {debugWarning("call not allowed\n"); return false;}; + virtual bool transmitSilenceBlock(char *data, unsigned int nevents, unsigned int offset) + {debugWarning("call not allowed\n"); return false;}; +protected: // some generic helpers + int provideSilenceToPort(Port *p, unsigned int offset, unsigned int nevents); + bool provideSilenceBlock(unsigned int nevents, unsigned int offset); + +private: + bool getFramesDry(unsigned int nbframes, int64_t ts); + bool getFramesWet(unsigned int nbframes, int64_t ts); + bool putFramesDry(unsigned int nbframes, int64_t ts); + bool putFramesWet(unsigned int nbframes, int64_t ts); + + bool transferSilence(unsigned int size); + +public: + // move to private? + bool xrunOccurred() { return m_in_xrun; }; + void handlerDied(); + +// the ISO interface (can we get rid of this?) +public: + int getChannel() {return m_channel;}; + bool setChannel(int c) + {m_channel = c; return true;}; + + virtual unsigned int getNbPacketsIsoXmitBuffer(); + virtual unsigned int getPacketsPerPeriod(); + virtual unsigned int getMaxPacketSize() = 0; +private: + int m_channel; + +protected: // FIXME: move to private + uint64_t m_last_timestamp; /// last timestamp (in ticks) +private: + uint64_t m_last_timestamp2; /// last timestamp (in ticks) +protected: + bool m_correct_last_timestamp; + uint64_t m_last_timestamp_at_period_ticks; // FIXME: still used? + +//--- data buffering and accounting +public: + void getBufferHeadTimestamp ( ffado_timestamp_t *ts, signed int *fc ) + {m_data_buffer->getBufferHeadTimestamp(ts, fc);}; + void getBufferTailTimestamp ( ffado_timestamp_t *ts, signed int *fc ) + {m_data_buffer->getBufferTailTimestamp(ts, fc);}; + + void setBufferTailTimestamp ( ffado_timestamp_t new_timestamp ) + {m_data_buffer->setBufferTailTimestamp(new_timestamp);}; + void setBufferHeadTimestamp ( ffado_timestamp_t new_timestamp ) + {m_data_buffer->setBufferHeadTimestamp(new_timestamp);}; +protected: + Util::TimestampedBuffer *m_data_buffer; + // the scratch buffer is temporary buffer space that can be + // used by any function. It's pre-allocated when the SP is created. + // the purpose is to avoid allocation of memory (or heap/stack) in + // an RT context + byte_t* m_scratch_buffer; + size_t m_scratch_buffer_size_bytes; + +protected: + // frame counter & sync stuff + public: + /** + * @brief Can this StreamProcessor handle a transfer of nframes frames? + * + * this function indicates if the streamprocessor can handle a transfer of + * nframes frames. It is used to detect underruns-to-be. + * + * @param nframes number of frames + * @return true if the StreamProcessor can handle this amount of frames + * false if it can't + */ + bool canClientTransferFrames(unsigned int nframes); + + /** + * \brief return the time until the next period boundary should be signaled (in microseconds) + * + * Return the time until the next period boundary signal. If this StreamProcessor + * is the current synchronization source, this function is called to + * determine when a buffer transfer can be made. When this value is + * smaller than 0, a period boundary is assumed to be crossed, hence a + * transfer can be made. + * + * \return the time in usecs + */ + int64_t getTimeUntilNextPeriodSignalUsecs(); + /** + * \brief return the time of the next period boundary (in microseconds) + * + * Returns the time of the next period boundary, in microseconds. The + * goal of this function is to determine the exact point of the period + * boundary. This is assumed to be the point at which the buffer transfer should + * take place, meaning that it can be used as a reference timestamp for transmitting + * StreamProcessors + * + * \return the time in usecs + */ + uint64_t getTimeAtPeriodUsecs(); + + /** + * \brief return the time of the next period boundary (in internal units) + * + * The same as getTimeAtPeriodUsecs() but in internal units. + * + * @return the time in internal units + */ + uint64_t getTimeAtPeriod(); + + uint64_t getTimeNow(); // FIXME: should disappear + + + /** + * Returns the sync delay. This is the time a syncsource + * delays a period signal, e.g. to cope with buffering. + * @return the sync delay + */ + unsigned int getSyncDelay() {return m_sync_delay;}; + /** + * sets the sync delay + * @param d sync delay + */ + void setSyncDelay(unsigned int d); + + /** + * @brief get the maximal frame latency + * + * The maximum frame latency is the maximum time that will elapse + * between the frame being received by the 1394 stack, and the moment this + * frame is presented to the StreamProcessor. + * + * For transmit SP's this is the maximum time that a frame is requested by + * the handler ahead of the time the frame is intended to be transmitted. + * + * This is useful to figure out how longer than the actual reception time + * we have to wait before trying to read the frame from the SP. + * + * @return maximal frame latency + */ + int getMaxFrameLatency(); + + float getTicksPerFrame(); + void setTicksPerFrame(float tpf); + + int getBufferFill(); + + // Child implementation interface + /** + * @brief prepare the child SP + * @return true if successful, false otherwise + * @pre the m_manager pointer points to a valid manager + * @post getEventsPerFrame() returns the correct value + * @post getEventSize() returns the correct value + * @post getUpdatePeriod() returns the correct value + * @post processPacketHeader(...) can be called + * @post processPacketData(...) can be called + */ + virtual bool prepareChild() = 0; + /** + * @brief get the number of events contained in one frame + * @return the number of events contained in one frame + */ + virtual unsigned int getEventsPerFrame() = 0; + + /** + * @brief get the size of one frame in bytes + * @return the size of one frame in bytes + */ + virtual unsigned int getEventSize() = 0; + + /** + * @brief get the nominal number of frames in a packet + * + * This is the amount of frames that is nominally present + * in one packet. It is recommended that in the receive handler + * you write this amount of frames when a valid packet has + * been received. (although this is not mandatory) + * + * @return the nominal number of frames in a packet + */ + virtual unsigned int getNominalFramesPerPacket() = 0; + + /** + * @brief get the nominal number of packets needed for a certain amount of frames + * @return the nominal number of packet necessary + */ + virtual unsigned int getNominalPacketsNeeded(unsigned int nframes); + + protected: + float m_ticks_per_frame; + unsigned int m_sync_delay; + private: + bool m_in_xrun; + +public: + // debug stuff + virtual void dumpInfo(); + virtual void printBufferInfo(); + virtual void setVerboseLevel(int l); + const char *getStateString() + {return ePSToString(getState());}; + const char *getTypeString() + {return ePTToString(getType());}; + + DECLARE_DEBUG_MODULE; +}; + +} + +#endif /* __FFADO_STREAMPROCESSOR__ */ + + Index: /branches/libffado-2.0/src/libstreaming/generic/Port.cpp =================================================================== --- /branches/libffado-2.0/src/libstreaming/generic/Port.cpp (revision 904) +++ /branches/libffado-2.0/src/libstreaming/generic/Port.cpp (revision 904) @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "Port.h" +#include "PortManager.h" + +#include +#include + +namespace Streaming { + +IMPL_DEBUG_MODULE( Port, Port, DEBUG_LEVEL_NORMAL ); + +Port::Port(PortManager& m, std::string name, + enum E_PortType porttype, enum E_Direction direction) + : m_Name( name ) + , m_disabled( true ) + , m_buffersize( 0 ) + , m_PortType( porttype ) + , m_Direction( direction ) + , m_buffer( NULL ) + , m_manager( m ) + , m_State( E_Created ) +{ + m_manager.registerPort(this); +} + +Port::~Port() { + debugOutput( DEBUG_LEVEL_VERBOSE, "deleting port %s\n", getName().c_str()); + m_manager.unregisterPort(this); +} + +/** + * The idea is that you set all port parameters, and then initialize the port. + * This allocates all resources and makes the port usable. However, you can't + * change the port properties anymore after this. + * + * @return true if successfull. false if not (all resources are freed). + */ +bool Port::init() { + debugOutput( DEBUG_LEVEL_VERBOSE, "Initialize port %s\n", m_Name.c_str()); + if (m_State != E_Created) { + debugFatal("Port (%s) not in E_Created state: %d\n", m_Name.c_str(), m_State); + return false; + } + + if (m_buffersize == 0) { + debugFatal("Cannot initialize a port with buffersize=0\n"); + return false; + } + + m_State = E_Initialized; + return true; +} + +bool Port::reset() { + return true; +} + +bool Port::setName(std::string name) { + debugOutput( DEBUG_LEVEL_VERBOSE, "Setting name to %s for port %s\n",name.c_str(),m_Name.c_str()); + + if (m_State != E_Created) { + debugFatal("Port (%s) not in E_Created state: %d\n",m_Name.c_str(),m_State); + return false; + } + m_Name=name; + return true; +} + +bool Port::setBufferSize(unsigned int newsize) { + debugOutput( DEBUG_LEVEL_VERBOSE, "Setting buffersize to %d for port %s\n",newsize,m_Name.c_str()); + if (m_State != E_Created) { + debugFatal("Port (%s) not in E_Created state: %d\n",m_Name.c_str(),m_State); + return false; + } + m_buffersize=newsize; + return true; +} + +unsigned int Port::getEventSize() { + return 4; // whether it's float, int24, midi or control, it's 4 +} + +// buffer handling api's for pointer buffers +/** + * Get the buffer address + * + * @param buff + */ +void *Port::getBufferAddress() { + return m_buffer; +}; + +/** + * Set the external buffer address. + * + * @param buff + */ +void Port::setBufferAddress(void *buff) { + m_buffer=buff; +} + +/// Enable the port. (this can be called anytime) +void +Port::enable() { + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Enabling port %s...\n",m_Name.c_str()); + m_disabled=false; +} + +/// Disable the port. (this can be called anytime) +void +Port::disable() { + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Disabling port %s...\n",m_Name.c_str()); + m_disabled=false; +} + +void Port::show() { + debugOutput(DEBUG_LEVEL_VERBOSE,"Name : %s\n", m_Name.c_str()); + debugOutput(DEBUG_LEVEL_VERBOSE,"Enabled? : %d\n", m_disabled); + debugOutput(DEBUG_LEVEL_VERBOSE,"State? : %d\n", m_State); + debugOutput(DEBUG_LEVEL_VERBOSE,"Buffer Size : %d\n", m_buffersize); + debugOutput(DEBUG_LEVEL_VERBOSE,"Event Size : %d\n", getEventSize()); + debugOutput(DEBUG_LEVEL_VERBOSE,"Port Type : %d\n", m_PortType); + debugOutput(DEBUG_LEVEL_VERBOSE,"Direction : %d\n", m_Direction); +} + +void Port::setVerboseLevel(int l) { + setDebugLevel(l); +} + +} Index: /branches/libffado-2.0/src/libstreaming/generic/PortManager.h =================================================================== --- /branches/libffado-2.0/src/libstreaming/generic/PortManager.h (revision 864) +++ /branches/libffado-2.0/src/libstreaming/generic/PortManager.h (revision 864) @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_PORTMANAGER__ +#define __FFADO_PORTMANAGER__ + +#include "Port.h" + +#include "debugmodule/debugmodule.h" + +#include + +namespace Streaming { + +typedef std::vector PortVector; +typedef std::vector::iterator PortVectorIterator; +/*! +\brief The Base Class for any class that maintains a collection of ports. + + Contains helper classes that allow the easy maintaining of Port collections. + +*/ +class PortManager { + +public: + + PortManager(); + virtual ~PortManager(); + + virtual bool makeNameUnique(Port *port); + virtual bool registerPort(Port *port); + virtual bool unregisterPort(Port *port); + + int getPortCount(enum Port::E_PortType); + int getPortCount(); + + Port *getPortAtIdx(unsigned int index); + + virtual bool resetPorts(); + virtual bool initPorts(); + virtual bool preparePorts(); + + virtual void setVerboseLevel(int l); + +protected: + PortVector m_Ports; + + DECLARE_DEBUG_MODULE; +}; + +} + +#endif /* __FFADO_PORTMANAGER__ */ + + Index: /branches/libffado-2.0/src/libstreaming/generic/Port.h =================================================================== --- /branches/libffado-2.0/src/libstreaming/generic/Port.h (revision 864) +++ /branches/libffado-2.0/src/libstreaming/generic/Port.h (revision 864) @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_PORT__ +#define __FFADO_PORT__ + +#include "libutil/ringbuffer.h" + +#include "debugmodule/debugmodule.h" + +#include +#include + +namespace Streaming { +class PortManager; + +/*! +\brief The Base Class for Ports + + Ports are the entities that provide the interface between the ISO streaming + layer and the datatype-specific layer. You can define port types by subclassing + the base port class. + + After creating a port, you have to set its parameters and then call the init() function. + This is because a port needs information from two sources to operate: + 1) the stream composition information from the AvDevice + 2) the streaming API setup (buffer type, data type, ...) + + \note There are not much virtual functions here because of the high frequency of + calling. We try to do everything with a base class getter, and a child class + setter. If this isn't possible, we do a static_cast. This however can only be + done inside the streamprocessor that handles the specific sub-class types of + the ports. i.e. by design you should make sure that the static_cast will be + OK. + +*/ +class Port { + +public: + /*! + \brief The port type + */ + enum E_PortType { + E_Audio, + E_Midi, + E_Control, + }; + + /*! + \brief The port direction + */ + enum E_Direction { + E_Playback, + E_Capture, + }; + + Port(PortManager&, std::string name, enum E_PortType, enum E_Direction); + + virtual ~Port(); + + + /// Enable the port. (this can be called anytime) + void enable(); + /// Disable the port. (this can be called anytime) + void disable(); + /// is the port disabled? (this can be called anytime) + bool isDisabled() {return m_disabled;}; + + /*! + \brief Initialize the port + */ + bool init(); + + bool prepare() {return true;}; + bool reset(); + + std::string getName() {return m_Name;}; + bool setName(std::string name); + + /** + * \brief returns the size of the events in the port buffer, in bytes + * + */ + unsigned int getEventSize(); + + enum E_PortType getPortType() {return m_PortType;}; ///< returns the port type (is fixed) + enum E_Direction getDirection() {return m_Direction;}; ///< returns the direction (is fixed) + + /** + * \brief returns the size of the port buffer + * + * counted in number of E_DataType units (events), not in bytes + * + */ + unsigned int getBufferSize() {return m_buffersize;}; + + /** + * \brief sets the size of the port buffer + * + * counted in number of E_DataType units, not in bytes + * + * if there is an external buffer assigned, it should + * be large enough + * if there is an internal buffer, it will be resized + * + * \note use before calling init() + */ + virtual bool setBufferSize(unsigned int); + + void setBufferAddress(void *buff); + void *getBufferAddress(); + + PortManager& getManager() { return m_manager; }; + + virtual void setVerboseLevel(int l); + virtual void show(); + +protected: + std::string m_Name; ///< Port name, [at construction] + bool m_disabled; ///< is the port disabled?, [anytime] + + unsigned int m_buffersize; + + enum E_PortType m_PortType; + enum E_Direction m_Direction; + + void *m_buffer; + + PortManager& m_manager; + + DECLARE_DEBUG_MODULE; + +// the state machine +protected: + enum EStates { + E_Created, + E_Initialized, + E_Prepared, + E_Running, + E_Error + }; + + enum EStates m_State; +}; + +/*! +\brief The Base Class for an Audio Port + + +*/ +class AudioPort : public Port { + +public: + + AudioPort(PortManager& m, std::string name, enum E_Direction direction) + : Port(m, name, E_Audio, direction) + {}; + + virtual ~AudioPort() {}; +}; + +/*! +\brief The Base Class for a Midi Port + + +*/ +class MidiPort : public Port { + +public: + + MidiPort(PortManager& m, std::string name, enum E_Direction direction) + : Port(m, name, E_Midi, direction) + {}; + virtual ~MidiPort() {}; +}; + +/*! +\brief The Base Class for a control port + + +*/ +class ControlPort : public Port { + +public: + + ControlPort(PortManager& m, std::string name, enum E_Direction direction) + : Port(m, name, E_Control, direction) + {}; + virtual ~ControlPort() {}; +}; + +} + +#endif /* __FFADO_PORT__ */ + + Index: /branches/libffado-2.0/src/libstreaming/generic/StreamProcessor.cpp =================================================================== --- /branches/libffado-2.0/src/libstreaming/generic/StreamProcessor.cpp (revision 1168) +++ /branches/libffado-2.0/src/libstreaming/generic/StreamProcessor.cpp (revision 1168) @@ -0,0 +1,1839 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "config.h" + +#include "StreamProcessor.h" +#include "../StreamProcessorManager.h" + +#include "devicemanager.h" + +#include "libieee1394/ieee1394service.h" +#include "libieee1394/IsoHandlerManager.h" +#include "libieee1394/cycletimer.h" + +#include "libutil/Time.h" + +#include "libutil/Atomic.h" + +#include +#include + +#define SIGNAL_ACTIVITY_SPM { \ + m_StreamProcessorManager.signalActivity(); \ +} +#define SIGNAL_ACTIVITY_ISO_XMIT { \ + m_IsoHandlerManager.signalActivityTransmit(); \ +} +#define SIGNAL_ACTIVITY_ISO_RECV { \ + m_IsoHandlerManager.signalActivityReceive(); \ +} +#define SIGNAL_ACTIVITY_ALL { \ + m_StreamProcessorManager.signalActivity(); \ + m_IsoHandlerManager.signalActivityTransmit(); \ + m_IsoHandlerManager.signalActivityReceive(); \ +} + +namespace Streaming { + +IMPL_DEBUG_MODULE( StreamProcessor, StreamProcessor, DEBUG_LEVEL_VERBOSE ); + +StreamProcessor::StreamProcessor(FFADODevice &parent, enum eProcessorType type) + : m_processor_type ( type ) + , m_state( ePS_Created ) + , m_next_state( ePS_Invalid ) + , m_cycle_to_switch_state( 0 ) + , m_Parent( parent ) + , m_1394service( parent.get1394Service() ) // local cache + , m_IsoHandlerManager( parent.get1394Service().getIsoHandlerManager() ) // local cache + , m_StreamProcessorManager( m_Parent.getDeviceManager().getStreamProcessorManager() ) // local cache + , m_local_node_id ( 0 ) // local cache + , m_channel( -1 ) + , m_last_timestamp( 0 ) + , m_last_timestamp2( 0 ) + , m_correct_last_timestamp( false ) + , m_scratch_buffer( NULL ) + , m_scratch_buffer_size_bytes( 0 ) + , m_ticks_per_frame( 0 ) + , m_sync_delay( 0 ) + , m_in_xrun( false ) +{ + // create the timestamped buffer and register ourselves as its client + m_data_buffer = new Util::TimestampedBuffer(this); +} + +StreamProcessor::~StreamProcessor() { + m_StreamProcessorManager.unregisterProcessor(this); + if(!m_IsoHandlerManager.unregisterStream(this)) { + debugOutput(DEBUG_LEVEL_VERBOSE,"Could not unregister stream processor with the Iso manager\n"); + } + + if (m_data_buffer) delete m_data_buffer; + if (m_scratch_buffer) delete[] m_scratch_buffer; +} + +void +StreamProcessor::handleBusReset() +{ + debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) handling busreset\n", this); + // for now, we try and make sure everything is cleanly shutdown + if(!stopRunning(-1)) { + debugError("Failed to stop SP\n"); + } + SIGNAL_ACTIVITY_ALL; +} + +void StreamProcessor::handlerDied() +{ + debugWarning("Handler died for %p\n", this); + m_state = ePS_Stopped; + m_in_xrun = true; +} + +uint64_t StreamProcessor::getTimeNow() { + return m_1394service.getCycleTimerTicks(); +} + +int StreamProcessor::getMaxFrameLatency() { + if (getType() == ePT_Receive) { + return (int)(m_IsoHandlerManager.getPacketLatencyForStream( this ) * TICKS_PER_CYCLE); + } else { + return (int)(m_IsoHandlerManager.getPacketLatencyForStream( this ) * TICKS_PER_CYCLE); + } +} + +unsigned int +StreamProcessor::getNominalPacketsNeeded(unsigned int nframes) +{ + unsigned int nominal_frames_per_second + = m_StreamProcessorManager.getNominalRate(); + uint64_t nominal_ticks_per_frame = TICKS_PER_SECOND / nominal_frames_per_second; + uint64_t nominal_ticks = nominal_ticks_per_frame * nframes; + uint64_t nominal_packets = nominal_ticks / TICKS_PER_CYCLE; + return nominal_packets; +} + +unsigned int +StreamProcessor::getPacketsPerPeriod() +{ + return getNominalPacketsNeeded(m_StreamProcessorManager.getPeriodSize()); +} + +unsigned int +StreamProcessor::getNbPacketsIsoXmitBuffer() +{ + // if we use one thread per packet, we can put every frame directly into the ISO buffer + // the waitForClient in IsoHandler will take care of the fact that the frames are + // not present in time + unsigned int packets_to_prebuffer = (getPacketsPerPeriod() * (m_StreamProcessorManager.getNbBuffers())) + 10; + debugOutput(DEBUG_LEVEL_VERBOSE, "Nominal prebuffer: %u\n", packets_to_prebuffer); + return packets_to_prebuffer; +} + +/*********************************************** + * Buffer management and manipulation * + ***********************************************/ +void StreamProcessor::flush() { + m_IsoHandlerManager.flushHandlerForStream(this); +} + +int StreamProcessor::getBufferFill() { + return m_data_buffer->getBufferFill(); +} + +int64_t +StreamProcessor::getTimeUntilNextPeriodSignalUsecs() +{ + uint64_t time_at_period=getTimeAtPeriod(); + + // we delay the period signal with the sync delay + // this makes that the period signals lag a little compared to reality + // ISO buffering causes the packets to be received at max + // m_handler->getWakeupInterval() later than the time they were received. + // hence their payload is available this amount of time later. However, the + // period boundary is predicted based upon earlier samples, and therefore can + // pass before these packets are processed. Adding this extra term makes that + // the period boundary is signalled later + time_at_period = addTicks(time_at_period, m_StreamProcessorManager.getSyncSource().getSyncDelay()); + + uint64_t cycle_timer=m_1394service.getCycleTimerTicks(); + + // calculate the time until the next period + int32_t until_next=diffTicks(time_at_period,cycle_timer); + + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "=> TAP=%11llu, CTR=%11llu, UTN=%11ld\n", + time_at_period, cycle_timer, until_next + ); + + // now convert to usecs + // don't use the mapping function because it only works + // for absolute times, not the relative time we are + // using here (which can also be negative). + return (int64_t)(((float)until_next) / TICKS_PER_USEC); +} + +void +StreamProcessor::setSyncDelay(unsigned int d) { + #ifdef DEBUG + unsigned int frames = (unsigned int)((float)d / getTicksPerFrame()); + debugOutput(DEBUG_LEVEL_VERBOSE, "Setting SP %p SyncDelay to %u ticks, %u frames\n", this, d, frames); + #endif + m_sync_delay = d; // FIXME: sync delay not necessary anymore +} + +uint64_t +StreamProcessor::getTimeAtPeriodUsecs() +{ + return (uint64_t)((float)getTimeAtPeriod() * TICKS_PER_USEC); +} + +uint64_t +StreamProcessor::getTimeAtPeriod() +{ + if (getType() == ePT_Receive) { + ffado_timestamp_t next_period_boundary = m_data_buffer->getTimestampFromHead(m_StreamProcessorManager.getPeriodSize()); + + #ifdef DEBUG + ffado_timestamp_t ts; + signed int fc; + m_data_buffer->getBufferTailTimestamp(&ts,&fc); + + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "=> NPD="TIMESTAMP_FORMAT_SPEC", LTS="TIMESTAMP_FORMAT_SPEC", FC=%5u, TPF=%f\n", + next_period_boundary, ts, fc, getTicksPerFrame() + ); + #endif + return (uint64_t)next_period_boundary; + } else { + ffado_timestamp_t next_period_boundary = m_data_buffer->getTimestampFromTail((m_StreamProcessorManager.getNbBuffers()-1) * m_StreamProcessorManager.getPeriodSize()); + + #ifdef DEBUG + ffado_timestamp_t ts; + signed int fc; + m_data_buffer->getBufferTailTimestamp(&ts,&fc); + + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "=> NPD="TIMESTAMP_FORMAT_SPEC", LTS="TIMESTAMP_FORMAT_SPEC", FC=%5u, TPF=%f\n", + next_period_boundary, ts, fc, getTicksPerFrame() + ); + #endif + return (uint64_t)next_period_boundary; + } +} + +float +StreamProcessor::getTicksPerFrame() +{ + assert(m_data_buffer != NULL); + return m_data_buffer->getRate(); +} + +void +StreamProcessor::setTicksPerFrame(float tpf) +{ + assert(m_data_buffer != NULL); + m_data_buffer->setRate(tpf); +} + +bool +StreamProcessor::canClientTransferFrames(unsigned int nbframes) +{ + bool can_transfer; + unsigned int fc = m_data_buffer->getFrameCounter(); + if (getType() == ePT_Receive) { + can_transfer = (fc >= nbframes); + } else { + // there has to be enough space to put the frames in + can_transfer = m_data_buffer->getBufferSize() - fc > nbframes; + // or the buffer is transparent + can_transfer |= m_data_buffer->isTransparent(); + } + + #ifdef DEBUG + if (!can_transfer) { + debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) cannot transfer since fc == %u, nbframes == %u\n", + this, ePTToString(getType()), fc, nbframes); + } + #endif + + return can_transfer; +} + +/*********************************************** + * I/O API * + ***********************************************/ + +// Packet transfer API +enum raw1394_iso_disposition +StreamProcessor::putPacket(unsigned char *data, unsigned int length, + unsigned char channel, unsigned char tag, unsigned char sy, + uint32_t pkt_ctr, + unsigned int dropped_cycles, unsigned int skipped) { + // bypass based upon state +#ifdef DEBUG + if (m_state == ePS_Invalid) { + debugError("Should not have state %s\n", ePSToString(m_state) ); + return RAW1394_ISO_ERROR; + } +#endif + // FIXME: isn't this also an error condition? + if (m_state == ePS_Created) { + return RAW1394_ISO_DEFER; + } + + // store the previous timestamp + m_last_timestamp2 = m_last_timestamp; + + // NOTE: synchronized switching is restricted to a 0.5 sec span (4000 cycles) + // it happens on the first 'good' cycle for the wait condition + // or on the first received cycle that is received afterwards (might be a problem) + + // check whether we are waiting for a stream to be disabled + if(m_state == ePS_WaitingForStreamDisable) { + // we then check whether we have to switch on this cycle + if (diffCycles(CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_cycle_to_switch_state) >= 0) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to DryRunning\n"); + m_next_state = ePS_DryRunning; + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + } else { + // not time to disable yet + } + // the received data can be discarded while waiting for the stream + // to be disabled + // similarly for dropped packets + return RAW1394_ISO_OK; + } + + // check whether we are waiting for a stream to be enabled + else if(m_state == ePS_WaitingForStreamEnable + && m_next_state == ePS_WaitingForStreamEnable) { + // we then check whether we have to switch on this cycle + if (diffCycles(CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_cycle_to_switch_state) >= 0) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to Running\n"); + m_next_state = ePS_Running; + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + } else { + // not time to enable yet + } + // we are dryRunning hence data should be processed in any case + } + + // check the packet header + enum eChildReturnValue result = processPacketHeader(data, length, tag, sy, pkt_ctr); + + // handle dropped cycles + if(dropped_cycles) { + // make sure the last_timestamp is corrected + m_correct_last_timestamp = true; + if (m_state == ePS_Running) { + // this is an xrun situation + m_in_xrun = true; + debugOutput(DEBUG_LEVEL_NORMAL, "Should update state to WaitingForStreamDisable due to dropped packet xrun\n"); + m_cycle_to_switch_state = CYCLE_TIMER_GET_CYCLES(pkt_ctr) + 1; // switch in the next cycle + m_next_state = ePS_WaitingForStreamDisable; + // execute the requested change + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + } + } + + if (result == eCRV_OK) { + #ifdef DEBUG + int ticks_per_packet = (int)(getTicksPerFrame() * getNominalFramesPerPacket()); + int diff = diffTicks(m_last_timestamp, m_last_timestamp2); + // display message if the difference between two successive tick + // values is more than 50 ticks. 1 sample at 48k is 512 ticks + // so 50 ticks = 10%, which is a rather large jitter value. + if(diff-ticks_per_packet > 50 || diff-ticks_per_packet < -50) { + debugOutput(DEBUG_LEVEL_VERBOSE, + "cy %04u rather large TSP difference TS=%011llu => TS=%011llu (%d, nom %d)\n", + CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp2, + m_last_timestamp, diff, ticks_per_packet); + } + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "%04u %011llu %011llu %d %d\n", + CYCLE_TIMER_GET_CYCLES(pkt_ctr), + m_last_timestamp2, m_last_timestamp, + diff, ticks_per_packet); + #endif + + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "RECV: CY=%04u TS=%011llu\n", + CYCLE_TIMER_GET_CYCLES(pkt_ctr), + m_last_timestamp); + + if(m_correct_last_timestamp) { + // they represent a discontinuity in the timestamps, and hence are + // to be dealt with + debugOutput(DEBUG_LEVEL_NORMAL, "(%p) Correcting timestamp for dropped cycles, discarding packet...\n", this); + m_data_buffer->setBufferTailTimestamp(substractTicks(m_last_timestamp, + (uint64_t)(getNominalFramesPerPacket() + * getTicksPerFrame()))); + m_correct_last_timestamp = false; + } + + // check whether we are waiting for a stream to startup + // this requires that the packet is good + if(m_state == ePS_WaitingForStream) { + // since we have a packet with an OK header, + // we can indicate that the stream started up + + // we then check whether we have to switch on this cycle + if (diffCycles(CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_cycle_to_switch_state) >= 0) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to DryRunning due to good packet\n"); + // hence go to the dryRunning state + m_next_state = ePS_DryRunning; + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + } else { + // not time (yet) to switch state + } + // in both cases we don't want to process the data + return RAW1394_ISO_OK; + } + + // check whether a state change has been requested + // note that only the wait state changes are synchronized with the cycles + else if(m_state != m_next_state) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state from %s to %s\n", + ePSToString(m_state), ePSToString(m_next_state)); + // execute the requested change + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + } + + // for all states that reach this we are allowed to + // do protocol specific data reception + enum eChildReturnValue result2 = processPacketData(data, length); + + // if an xrun occured, switch to the dryRunning state and + // allow for the xrun to be picked up + if (result2 == eCRV_XRun) { + debugOutput(DEBUG_LEVEL_NORMAL, "processPacketData xrun\n"); + m_in_xrun = true; + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to WaitingForStreamDisable due to data xrun\n"); + m_cycle_to_switch_state = CYCLE_TIMER_GET_CYCLES(pkt_ctr)+1; // switch in the next cycle + m_next_state = ePS_WaitingForStreamDisable; + // execute the requested change + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + return RAW1394_ISO_DEFER; + } else if(result2 == eCRV_OK) { + // no problem here + // FIXME: cache the period size? + unsigned int periodsize = m_StreamProcessorManager.getPeriodSize(); + unsigned int bufferfill = m_data_buffer->getBufferFill(); + if(bufferfill >= periodsize) { + debugOutputExtreme(DEBUG_LEVEL_VERBOSE, "signal activity, %d>%d\n", bufferfill, periodsize); + SIGNAL_ACTIVITY_SPM; + return RAW1394_ISO_DEFER; + } + return RAW1394_ISO_OK; + } else { + debugError("Invalid response\n"); + return RAW1394_ISO_ERROR; + } + } else if(result == eCRV_Invalid) { + // apparently we don't have to do anything when the packets are not valid + return RAW1394_ISO_OK; + } else { + debugError("Invalid response\n"); + return RAW1394_ISO_ERROR; + } + debugError("reached the unreachable\n"); + return RAW1394_ISO_ERROR; +} + +enum raw1394_iso_disposition +StreamProcessor::getPacket(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + uint32_t pkt_ctr, unsigned int dropped_cycles, + unsigned int skipped, unsigned int max_length) { + if (pkt_ctr == 0xFFFFFFFF) { + *tag = 0; + *sy = 0; + *length = 0; + return RAW1394_ISO_OK; + } + uint64_t prev_timestamp; + + // note that we can ignore skipped cycles since + // the protocol will take care of that + if (dropped_cycles > 0) { + // HACK: this should not be necessary, since the header generation functions should trigger the xrun. + // but apparently there are some issues with the 1394 stack + m_in_xrun = true; + if(m_state == ePS_Running) { + debugShowBackLogLines(200); + debugOutput(DEBUG_LEVEL_NORMAL, "dropped packets xrun\n"); + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to WaitingForStreamDisable due to dropped packets xrun\n"); + m_cycle_to_switch_state = CYCLE_TIMER_GET_CYCLES(pkt_ctr) + 1; + m_next_state = ePS_WaitingForStreamDisable; + // execute the requested change + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + goto send_empty_packet; + } + } + +#ifdef DEBUG + // bypass based upon state + if (m_state == ePS_Invalid) { + debugError("Should not have state %s\n", ePSToString(m_state) ); + return RAW1394_ISO_ERROR; + } +#endif + + // FIXME: can this happen? + if (m_state == ePS_Created) { + *tag = 0; + *sy = 0; + *length = 0; + return RAW1394_ISO_DEFER; + } + + // normal processing + + // store the previous timestamp + // keep the old value here, update m_last_timestamp2 only when + // a valid packet will be sent + prev_timestamp = m_last_timestamp; + + // NOTE: synchronized switching is restricted to a 0.5 sec span (4000 cycles) + // it happens on the first 'good' cycle for the wait condition + // or on the first received cycle that is received afterwards (might be a problem) + + // check whether we are waiting for a stream to be disabled + if(m_state == ePS_WaitingForStreamDisable) { + // we then check whether we have to switch on this cycle + if (diffCycles(CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_cycle_to_switch_state) >= 0) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to DryRunning\n"); + m_next_state = ePS_DryRunning; + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + } + // generate the silent packet header + enum eChildReturnValue result = generateSilentPacketHeader(data, length, tag, sy, pkt_ctr); + if (result == eCRV_Packet) { + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "XMIT SILENT: CY=%04u TS=%011llu\n", + cycle, m_last_timestamp); + + // assumed not to xrun + generateSilentPacketData(data, length); + return RAW1394_ISO_OK; + // FIXME: PP: I think this [empty packet] should also be a possibility + // JMW: yes, it should. MOTU needs it for a clean shutdown. + } else if (result == eCRV_EmptyPacket) { + goto send_empty_packet; + } else { + debugError("Invalid return value: %d\n", result); + return RAW1394_ISO_ERROR; + } + } + // check whether we are waiting for a stream to be enabled + else if(m_state == ePS_WaitingForStreamEnable + && m_next_state == ePS_WaitingForStreamEnable) { + // we then check whether we have to switch on this cycle + if (diffCycles(CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_cycle_to_switch_state) >= 0) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to Running\n"); + m_next_state = ePS_Running; + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + } else { + // not time to enable yet + } + // we are dryRunning hence data should be processed in any case + } + // check whether we are waiting for a stream to startup + else if(m_state == ePS_WaitingForStream) { + // check whether we have to switch on this cycle + if ((diffCycles(CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_cycle_to_switch_state) >= 0)) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to WaitingForStream to DryRunning\n"); + // hence go to the dryRunning state + m_next_state = ePS_DryRunning; + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + } else { + // not time (yet) to switch state + } + } + else if(m_state == ePS_Running) { + // check the packet header + enum eChildReturnValue result = generatePacketHeader(data, length, tag, sy, pkt_ctr); + if (result == eCRV_Packet || result == eCRV_Defer) { + debugOutputExtreme(DEBUG_LEVEL_VERBOSE, + "XMIT: CY=%04u TS=%011llu\n", + cycle, m_last_timestamp); + + // valid packet timestamp + m_last_timestamp2 = prev_timestamp; + + // check whether a state change has been requested + // note that only the wait state changes are synchronized with the cycles + if(m_state != m_next_state) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state from %s to %s\n", + ePSToString(m_state), ePSToString(m_next_state)); + // execute the requested change + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + } + + enum eChildReturnValue result2 = generatePacketData(data, length); + // if an xrun occured, switch to the dryRunning state and + // allow for the xrun to be picked up + if (result2 == eCRV_XRun) { + debugOutput(DEBUG_LEVEL_NORMAL, "generatePacketData xrun\n"); + m_in_xrun = true; + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to WaitingForStreamDisable due to data xrun\n"); + m_cycle_to_switch_state = CYCLE_TIMER_GET_CYCLES(pkt_ctr) + 1; // switch in the next cycle + m_next_state = ePS_WaitingForStreamDisable; + // execute the requested change + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + goto send_empty_packet; + } + #ifdef DEBUG + int ticks_per_packet = (int)(getTicksPerFrame() * getNominalFramesPerPacket()); + int diff = diffTicks(m_last_timestamp, m_last_timestamp2); + // display message if the difference between two successive tick + // values is more than 50 ticks. 1 sample at 48k is 512 ticks + // so 50 ticks = 10%, which is a rather large jitter value. + if(diff-ticks_per_packet > 50 || diff-ticks_per_packet < -50) { + debugOutput(DEBUG_LEVEL_VERBOSE, + "cy %04d, rather large TSP difference TS=%011llu => TS=%011llu (%d, nom %d)\n", + CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp2, + m_last_timestamp, diff, ticks_per_packet); + } + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "%04d %011llu %011llu %d %d\n", + CYCLE_TIMER_GET_CYCLES(pkt_ctr), m_last_timestamp2, + m_last_timestamp, diff, ticks_per_packet); + #endif + + // skip queueing packets if we detect that there are not enough frames + // available + if(result2 == eCRV_Defer || result == eCRV_Defer) { + return RAW1394_ISO_DEFER; + } else { + return RAW1394_ISO_OK; + } + } else if (result == eCRV_XRun) { // pick up the possible xruns + debugOutput(DEBUG_LEVEL_NORMAL, "generatePacketHeader xrun\n"); + m_in_xrun = true; + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to WaitingForStreamDisable due to header xrun\n"); + m_cycle_to_switch_state = CYCLE_TIMER_GET_CYCLES(pkt_ctr) + 1; // switch in the next cycle + m_next_state = ePS_WaitingForStreamDisable; + // execute the requested change + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + } else if (result == eCRV_EmptyPacket) { + if(m_state != m_next_state) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state from %s to %s\n", + ePSToString(m_state), ePSToString(m_next_state)); + // execute the requested change + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + } + goto send_empty_packet; + } else if (result == eCRV_Again) { + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "have to retry cycle %d\n", CYCLE_TIMER_GET_CYCLES(pkt_ctr)); + if(m_state != m_next_state) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state from %s to %s\n", + ePSToString(m_state), ePSToString(m_next_state)); + // execute the requested change + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + } +// usleep(125); // only when using thread-per-handler +// return RAW1394_ISO_AGAIN; + generateEmptyPacketHeader(data, length, tag, sy, pkt_ctr); + generateEmptyPacketData(data, length); + return RAW1394_ISO_DEFER; + } else { + debugError("Invalid return value: %d\n", result); + return RAW1394_ISO_ERROR; + } + } + // we are not running, so send an empty packet + // we should generate a valid packet any time +send_empty_packet: + // note that only the wait state changes are synchronized with the cycles + if(m_state != m_next_state) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state from %s to %s\n", + ePSToString(m_state), ePSToString(m_next_state)); + // execute the requested change + if (!updateState()) { // we are allowed to change the state directly + debugError("Could not update state!\n"); + return RAW1394_ISO_ERROR; + } + } + debugOutputExtreme(DEBUG_LEVEL_VERBOSE, + "XMIT EMPTY: CY=%04u\n", + CYCLE_TIMER_GET_CYCLES(pkt_ctr)); + + generateEmptyPacketHeader(data, length, tag, sy, pkt_ctr); + generateEmptyPacketData(data, length); + return RAW1394_ISO_OK; +} + +// Frame Transfer API +/** + * Transfer a block of frames from the event buffer to the port buffers + * @param nbframes number of frames to transfer + * @param ts the timestamp that the LAST frame in the block should have + * @return + */ +bool StreamProcessor::getFrames(unsigned int nbframes, int64_t ts) { + bool result; + debugOutputExtreme( DEBUG_LEVEL_VERBOSE, + "(%p, %s) getFrames(%d, %11llu)\n", + this, getTypeString(), nbframes, ts); + assert( getType() == ePT_Receive ); + if(isDryRunning()) result = getFramesDry(nbframes, ts); + else result = getFramesWet(nbframes, ts); + SIGNAL_ACTIVITY_ISO_RECV; + return result; +} + +bool StreamProcessor::getFramesWet(unsigned int nbframes, int64_t ts) { +// FIXME: this should be done somewhere else +#ifdef DEBUG + uint64_t ts_expected; + signed int fc; + int32_t lag_ticks; + float lag_frames; + + // in order to sync up multiple received streams, we should + // use the ts parameter. It specifies the time of the block's + // last sample. + float srate = m_StreamProcessorManager.getSyncSource().getTicksPerFrame(); + assert(srate != 0.0); + int64_t this_block_length_in_ticks = (int64_t)(((float)nbframes) * srate); + + ffado_timestamp_t ts_head_tmp; + m_data_buffer->getBufferHeadTimestamp(&ts_head_tmp, &fc); + ts_expected = addTicks((uint64_t)ts_head_tmp, this_block_length_in_ticks); + + lag_ticks = diffTicks(ts, ts_expected); + lag_frames = (((float)lag_ticks) / srate); + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "stream (%p): drifts %6d ticks = %10.5f frames (rate=%10.5f), %lld, %llu, %d\n", + this, lag_ticks, lag_frames, srate, ts, ts_expected, fc); + if (lag_frames >= 1.0) { + // the stream lags + debugOutput(DEBUG_LEVEL_VERBOSE, "stream (%p): lags with %6d ticks = %10.5f frames (rate=%10.5f), %lld, %llu, %d\n", + this, lag_ticks, lag_frames, srate, ts, ts_expected, fc); + } else if (lag_frames <= -1.0) { + // the stream leads + debugOutput(DEBUG_LEVEL_VERBOSE, "stream (%p): leads with %6d ticks = %10.5f frames (rate=%10.5f), %lld, %llu, %d\n", + this, lag_ticks, lag_frames, srate, ts, ts_expected, fc); + } +#endif + // ask the buffer to process nbframes of frames + // using it's registered client's processReadBlock(), + // which should be ours + m_data_buffer->blockProcessReadFrames(nbframes); + return true; +} + +bool StreamProcessor::getFramesDry(unsigned int nbframes, int64_t ts) +{ + debugOutputExtreme(DEBUG_LEVEL_VERY_VERBOSE, + "stream (%p): dry run %d frames (@ ts=%lld)\n", + this, nbframes, ts); + // dry run on this side means that we put silence in all enabled ports + // since there is do data put into the ringbuffer in the dry-running state + return provideSilenceBlock(nbframes, 0); +} + +bool +StreamProcessor::dropFrames(unsigned int nbframes, int64_t ts) +{ + bool result; + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "StreamProcessor::dropFrames(%d, %lld)\n", nbframes, ts); + result = m_data_buffer->dropFrames(nbframes); + SIGNAL_ACTIVITY_ISO_RECV; + return result; +} + +bool StreamProcessor::putFrames(unsigned int nbframes, int64_t ts) +{ + bool result; + debugOutputExtreme( DEBUG_LEVEL_VERBOSE, + "(%p, %s) putFrames(%d, %11llu)\n", + this, getTypeString(), nbframes, ts); + assert( getType() == ePT_Transmit ); + if(isDryRunning()) result = putFramesDry(nbframes, ts); + else result = putFramesWet(nbframes, ts); + SIGNAL_ACTIVITY_ISO_XMIT; + return result; +} + +bool +StreamProcessor::putFramesWet(unsigned int nbframes, int64_t ts) +{ + debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, + "StreamProcessor::putFramesWet(%d, %llu)\n", + nbframes, ts); + // transfer the data + m_data_buffer->blockProcessWriteFrames(nbframes, ts); + debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, + " New timestamp: %llu\n", ts); + return true; // FIXME: what about failure? +} + +bool +StreamProcessor::putFramesDry(unsigned int nbframes, int64_t ts) +{ + debugOutputExtreme(DEBUG_LEVEL_ULTRA_VERBOSE, + "StreamProcessor::putFramesDry(%d, %llu)\n", + nbframes, ts); + // do nothing + return true; +} + +bool +StreamProcessor::putSilenceFrames(unsigned int nbframes, int64_t ts) +{ + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, + "StreamProcessor::putSilenceFrames(%d, %llu)\n", + nbframes, ts); + + size_t bytes_per_frame = getEventSize() * getEventsPerFrame(); + unsigned int scratch_buffer_size_frames = m_scratch_buffer_size_bytes / bytes_per_frame; + + if (nbframes > scratch_buffer_size_frames) { + debugError("nframes (%u) > scratch_buffer_size_frames (%u)\n", + nbframes, scratch_buffer_size_frames); + } + + assert(m_scratch_buffer); + if(!transmitSilenceBlock((char *)m_scratch_buffer, nbframes, 0)) { + debugError("Could not prepare silent block\n"); + return false; + } + if(!m_data_buffer->writeFrames(nbframes, (char *)m_scratch_buffer, ts)) { + debugError("Could not write silent block\n"); + return false; + } + + SIGNAL_ACTIVITY_ISO_XMIT; + return true; +} + +bool +StreamProcessor::shiftStream(int nbframes) +{ + bool result; + if(nbframes == 0) return true; + if(nbframes > 0) { + result = m_data_buffer->dropFrames(nbframes); + SIGNAL_ACTIVITY_ALL; + return result; + } else { + result = true; + while(nbframes++) { + result &= m_data_buffer->writeDummyFrame(); + } + SIGNAL_ACTIVITY_ALL; + return result; + } +} + +/** + * @brief write silence events to the stream ringbuffers. + */ +bool StreamProcessor::provideSilenceBlock(unsigned int nevents, unsigned int offset) +{ + bool no_problem=true; + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) { + if((*it)->isDisabled()) {continue;}; + + if(provideSilenceToPort((*it), offset, nevents)) { + debugWarning("Could not put silence into to port %s",(*it)->getName().c_str()); + no_problem=false; + } + } + return no_problem; +} + +int +StreamProcessor::provideSilenceToPort(Port *p, unsigned int offset, unsigned int nevents) +{ + unsigned int j=0; + switch(p->getPortType()) { + default: + debugError("Invalid port type: %d\n", p->getPortType()); + return -1; + case Port::E_Midi: + case Port::E_Control: + { + quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); + assert(nevents + offset <= p->getBufferSize()); + buffer+=offset; + + for(j = 0; j < nevents; j += 1) { + *(buffer)=0; + buffer++; + } + } + break; + case Port::E_Audio: + switch(m_StreamProcessorManager.getAudioDataType()) { + case StreamProcessorManager::eADT_Int24: + { + quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); + assert(nevents + offset <= p->getBufferSize()); + buffer+=offset; + + for(j = 0; j < nevents; j += 1) { + *(buffer)=0; + buffer++; + } + } + break; + case StreamProcessorManager::eADT_Float: + { + float *buffer=(float *)(p->getBufferAddress()); + assert(nevents + offset <= p->getBufferSize()); + buffer+=offset; + + for(j = 0; j < nevents; j += 1) { + *buffer = 0.0; + buffer++; + } + } + break; + } + break; + } + return 0; +} + +/*********************************************** + * State related API * + ***********************************************/ +bool StreamProcessor::init() +{ + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "init...\n"); + + if(!m_IsoHandlerManager.registerStream(this)) { + debugOutput(DEBUG_LEVEL_VERBOSE,"Could not register stream processor with the Iso manager\n"); + return false; + } + if(!m_StreamProcessorManager.registerProcessor(this)) { + debugOutput(DEBUG_LEVEL_VERBOSE,"Could not register stream processor with the SP manager\n"); + return false; + } + + // initialization can be done without requesting it + // from the packet loop + m_next_state = ePS_Created; + return true; +} + +bool StreamProcessor::prepare() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Prepare SP (%p)...\n", this); + + // make the scratch buffer one period of frames long + m_scratch_buffer_size_bytes = m_StreamProcessorManager.getPeriodSize() * getEventsPerFrame() * getEventSize(); + debugOutput( DEBUG_LEVEL_VERBOSE, " Allocate scratch buffer of %d quadlets\n"); + if(m_scratch_buffer) delete[] m_scratch_buffer; + m_scratch_buffer = new byte_t[m_scratch_buffer_size_bytes]; + if(m_scratch_buffer == NULL) { + debugFatal("Could not allocate scratch buffer\n"); + return false; + } + + // set the parameters of ports we can: + // we want the audio ports to be period buffered, + // and the midi ports to be packet buffered + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + debugOutput(DEBUG_LEVEL_VERBOSE, "Setting up port %s\n",(*it)->getName().c_str()); + if(!(*it)->setBufferSize(m_StreamProcessorManager.getPeriodSize())) { + debugFatal("Could not set buffer size to %d\n",m_StreamProcessorManager.getPeriodSize()); + return false; + } + } + // the API specific settings of the ports should already be set, + // as this is called from the processorManager->prepare() + // so we can init the ports + if(!PortManager::initPorts()) { + debugFatal("Could not initialize ports\n"); + return false; + } + + if (!prepareChild()) { + debugFatal("Could not prepare child\n"); + return false; + } + + debugOutput( DEBUG_LEVEL_VERBOSE, "Prepared for:\n"); + debugOutput( DEBUG_LEVEL_VERBOSE, " Samplerate: %d\n", + m_StreamProcessorManager.getNominalRate()); + debugOutput( DEBUG_LEVEL_VERBOSE, " PeriodSize: %d, NbBuffers: %d\n", + m_StreamProcessorManager.getPeriodSize(), m_StreamProcessorManager.getNbBuffers()); + debugOutput( DEBUG_LEVEL_VERBOSE, " Port: %d, Channel: %d\n", + m_1394service.getPort(), m_channel); + + // initialization can be done without requesting it + // from the packet loop + m_next_state = ePS_Stopped; + return updateState(); +} + +bool +StreamProcessor::scheduleStateTransition(enum eProcessorState state, uint64_t time_instant) +{ + // first set the time, since in the packet loop we first check m_state == m_next_state before + // using the time + m_cycle_to_switch_state = TICKS_TO_CYCLES(time_instant); + m_next_state = state; + // wake up any threads that might be waiting on data in the buffers + // since a state transition can cause data to become available + SIGNAL_ACTIVITY_ALL; + return true; +} + +bool +StreamProcessor::waitForState(enum eProcessorState state, unsigned int timeout_ms) +{ + debugOutput(DEBUG_LEVEL_VERBOSE, "Waiting for state %s\n", ePSToString(state)); + int cnt = timeout_ms; + while (m_state != state && cnt) { + SleepRelativeUsec(1000); + cnt--; + } + if(cnt==0) { + debugOutput(DEBUG_LEVEL_VERBOSE, " Timeout\n"); + return false; + } + return true; +} + +bool StreamProcessor::scheduleStartDryRunning(int64_t t) { + uint64_t tx; + if (t < 0) { + tx = addTicks(m_1394service.getCycleTimerTicks(), 200 * TICKS_PER_CYCLE); + } else { + tx = t; + } + uint64_t start_handler_ticks = substractTicks(tx, 100 * TICKS_PER_CYCLE); + + debugOutput(DEBUG_LEVEL_VERBOSE,"for %s SP (%p)\n", ePTToString(getType()), this); + #ifdef DEBUG + uint64_t now = m_1394service.getCycleTimerTicks(); + debugOutput(DEBUG_LEVEL_VERBOSE," Now : %011llu (%03us %04uc %04ut)\n", + now, + (unsigned int)TICKS_TO_SECS(now), + (unsigned int)TICKS_TO_CYCLES(now), + (unsigned int)TICKS_TO_OFFSET(now)); + debugOutput(DEBUG_LEVEL_VERBOSE," Start at : %011llu (%03us %04uc %04ut)\n", + tx, + (unsigned int)TICKS_TO_SECS(tx), + (unsigned int)TICKS_TO_CYCLES(tx), + (unsigned int)TICKS_TO_OFFSET(tx)); + #endif + if (m_state == ePS_Stopped) { + if(!m_IsoHandlerManager.startHandlerForStream( + this, TICKS_TO_CYCLES(start_handler_ticks))) { + debugError("Could not start handler for SP %p\n", this); + return false; + } + return scheduleStateTransition(ePS_WaitingForStream, tx); + } else if (m_state == ePS_Running) { + return scheduleStateTransition(ePS_WaitingForStreamDisable, tx); + } else if (m_state == ePS_DryRunning) { + debugOutput(DEBUG_LEVEL_VERBOSE, " %p already in DryRunning state\n", this); + return true; + } else if (m_state == ePS_WaitingForStreamEnable) { + debugOutput(DEBUG_LEVEL_VERBOSE, " %p still waiting to switch to Running state\n", this); + // this will happen immediately + return scheduleStateTransition(ePS_DryRunning, tx); + } else if (m_state == ePS_WaitingForStreamDisable) { + debugOutput(DEBUG_LEVEL_VERBOSE, " %p already waiting to switch to DryRunning state\n", this); + return true; + } else { + debugError("Cannot switch to ePS_DryRunning from %s\n", ePSToString(m_state)); + return false; + } +} + +bool StreamProcessor::scheduleStartRunning(int64_t t) { + uint64_t tx; + if (t < 0) { + tx = addTicks(m_1394service.getCycleTimerTicks(), 200 * TICKS_PER_CYCLE); + } else { + tx = t; + } + debugOutput(DEBUG_LEVEL_VERBOSE,"for %s SP (%p)\n", ePTToString(getType()), this); + #ifdef DEBUG + uint64_t now = m_1394service.getCycleTimerTicks(); + debugOutput(DEBUG_LEVEL_VERBOSE," Now : %011llu (%03us %04uc %04ut)\n", + now, + (unsigned int)TICKS_TO_SECS(now), + (unsigned int)TICKS_TO_CYCLES(now), + (unsigned int)TICKS_TO_OFFSET(now)); + debugOutput(DEBUG_LEVEL_VERBOSE," Start at : %011llu (%03us %04uc %04ut)\n", + tx, + (unsigned int)TICKS_TO_SECS(tx), + (unsigned int)TICKS_TO_CYCLES(tx), + (unsigned int)TICKS_TO_OFFSET(tx)); + #endif + return scheduleStateTransition(ePS_WaitingForStreamEnable, tx); +} + +bool StreamProcessor::scheduleStopDryRunning(int64_t t) { + uint64_t tx; + if (t < 0) { + tx = addTicks(m_1394service.getCycleTimerTicks(), 2000 * TICKS_PER_CYCLE); + } else { + tx = t; + } + debugOutput(DEBUG_LEVEL_VERBOSE,"for %s SP (%p)\n", ePTToString(getType()), this); + #ifdef DEBUG + uint64_t now = m_1394service.getCycleTimerTicks(); + debugOutput(DEBUG_LEVEL_VERBOSE," Now : %011llu (%03us %04uc %04ut)\n", + now, + (unsigned int)TICKS_TO_SECS(now), + (unsigned int)TICKS_TO_CYCLES(now), + (unsigned int)TICKS_TO_OFFSET(now)); + debugOutput(DEBUG_LEVEL_VERBOSE," Stop at : %011llu (%03us %04uc %04ut)\n", + tx, + (unsigned int)TICKS_TO_SECS(tx), + (unsigned int)TICKS_TO_CYCLES(tx), + (unsigned int)TICKS_TO_OFFSET(tx)); + #endif + + return scheduleStateTransition(ePS_Stopped, tx); +} + +bool StreamProcessor::scheduleStopRunning(int64_t t) { + uint64_t tx; + if (t < 0) { + tx = addTicks(m_1394service.getCycleTimerTicks(), 2000 * TICKS_PER_CYCLE); + } else { + tx = t; + } + debugOutput(DEBUG_LEVEL_VERBOSE,"for %s SP (%p)\n", ePTToString(getType()), this); + #ifdef DEBUG + uint64_t now = m_1394service.getCycleTimerTicks(); + debugOutput(DEBUG_LEVEL_VERBOSE," Now : %011llu (%03us %04uc %04ut)\n", + now, + (unsigned int)TICKS_TO_SECS(now), + (unsigned int)TICKS_TO_CYCLES(now), + (unsigned int)TICKS_TO_OFFSET(now)); + debugOutput(DEBUG_LEVEL_VERBOSE," Stop at : %011llu (%03us %04uc %04ut)\n", + tx, + (unsigned int)TICKS_TO_SECS(tx), + (unsigned int)TICKS_TO_CYCLES(tx), + (unsigned int)TICKS_TO_OFFSET(tx)); + #endif + return scheduleStateTransition(ePS_WaitingForStreamDisable, tx); +} + +bool StreamProcessor::startDryRunning(int64_t t) { + if(getState() == ePS_DryRunning) { + // already in the correct state + return true; + } + if(!scheduleStartDryRunning(t)) { + debugError("Could not schedule transition\n"); + return false; + } + if(!waitForState(ePS_DryRunning, 2000)) { + debugError(" Timeout while waiting for %s\n", ePSToString(ePS_DryRunning)); + return false; + } + return true; +} + +bool StreamProcessor::startRunning(int64_t t) { + if(getState() == ePS_Running) { + // already in the correct state + return true; + } + if(!scheduleStartRunning(t)) { + debugError("Could not schedule transition\n"); + return false; + } + if(!waitForState(ePS_Running, 2000)) { + debugError(" Timeout while waiting for %s\n", ePSToString(ePS_Running)); + return false; + } + return true; +} + +bool StreamProcessor::stopDryRunning(int64_t t) { + if(getState() == ePS_Stopped) { + // already in the correct state + return true; + } + if(!scheduleStopDryRunning(t)) { + debugError("Could not schedule transition\n"); + return false; + } + if(!waitForState(ePS_Stopped, 2000)) { + debugError(" Timeout while waiting for %s\n", ePSToString(ePS_Stopped)); + return false; + } + return true; +} + +bool StreamProcessor::stopRunning(int64_t t) { + if(getState() == ePS_DryRunning) { + // already in the correct state + return true; + } + if(!scheduleStopRunning(t)) { + debugError("Could not schedule transition\n"); + return false; + } + if(!waitForState(ePS_DryRunning, 2000)) { + debugError(" Timeout while waiting for %s\n", ePSToString(ePS_DryRunning)); + return false; + } + return true; +} + + +// internal state API + +/** + * @brief Enter the ePS_Stopped state + * @return true if successful, false if not + * + * @pre none + * + * @post the buffer and the isostream are ready for use. + * @post all dynamic structures have been allocated successfully + * @post the buffer is transparent and empty, and all parameters are set + * to the correct initial/nominal values. + * + */ +bool +StreamProcessor::doStop() +{ + float ticks_per_frame; + unsigned int ringbuffer_size_frames = (m_StreamProcessorManager.getNbBuffers() + 1) * m_StreamProcessorManager.getPeriodSize(); + + debugOutput(DEBUG_LEVEL_VERBOSE, "Enter from state: %s\n", ePSToString(m_state)); + bool result = true; + + switch(m_state) { + case ePS_Created: + assert(m_data_buffer); + + // prepare the framerate estimate + ticks_per_frame = (TICKS_PER_SECOND*1.0) / ((float)m_StreamProcessorManager.getNominalRate()); + m_ticks_per_frame = ticks_per_frame; + m_local_node_id= m_1394service.getLocalNodeId() & 0x3f; + m_correct_last_timestamp = false; + + debugOutput(DEBUG_LEVEL_VERBOSE,"Initializing remote ticks/frame to %f\n", ticks_per_frame); + + // initialize internal buffer + result &= m_data_buffer->setBufferSize(ringbuffer_size_frames); + + result &= m_data_buffer->setEventSize( getEventSize() ); + result &= m_data_buffer->setEventsPerFrame( getEventsPerFrame() ); + if(getType() == ePT_Receive) { + result &= m_data_buffer->setUpdatePeriod( getNominalFramesPerPacket() ); + } else { + result &= m_data_buffer->setUpdatePeriod( m_StreamProcessorManager.getPeriodSize() ); + } + result &= m_data_buffer->setNominalRate(ticks_per_frame); + result &= m_data_buffer->setWrapValue(128L*TICKS_PER_SECOND); + result &= m_data_buffer->prepare(); // FIXME: the name + + break; + case ePS_DryRunning: + if(!m_IsoHandlerManager.stopHandlerForStream(this)) { + debugError("Could not stop handler for SP %p\n", this); + return false; + } + break; + default: + debugError("Entry from invalid state: %s\n", ePSToString(m_state)); + return false; + } + + result &= m_data_buffer->clearBuffer(); // FIXME: don't like the reset() name + // make the buffer transparent + m_data_buffer->setTransparent(true); + + // reset all ports + result &= PortManager::preparePorts(); + + m_state = ePS_Stopped; + #ifdef DEBUG + if (getDebugLevel() >= DEBUG_LEVEL_VERBOSE) { + debugOutput(DEBUG_LEVEL_VERBOSE, "State switch complete, dumping SP info...\n"); + dumpInfo(); + } + #endif + SIGNAL_ACTIVITY_ALL; + return result; +} + +/** + * @brief Enter the ePS_WaitingForStream state + * @return true if successful, false if not + * + * @pre all dynamic data structures are allocated successfully + * + * @post + * + */ +bool +StreamProcessor::doWaitForRunningStream() +{ + debugOutput(DEBUG_LEVEL_VERBOSE, "Enter from state: %s\n", ePSToString(m_state)); + switch(m_state) { + case ePS_Stopped: + // we have to start waiting for an incoming stream + // this basically means nothing, the state change will + // be picked up by the packet iterator + break; + default: + debugError("Entry from invalid state: %s\n", ePSToString(m_state)); + return false; + } + m_state = ePS_WaitingForStream; + #ifdef DEBUG + if (getDebugLevel() >= DEBUG_LEVEL_VERBOSE) { + debugOutput(DEBUG_LEVEL_VERBOSE, "State switch complete, dumping SP info...\n"); + dumpInfo(); + } + #endif + SIGNAL_ACTIVITY_ALL; + return true; +} + +/** + * @brief Enter the ePS_DryRunning state + * @return true if successful, false if not + * + * @pre + * + * @post + * + */ +bool +StreamProcessor::doDryRunning() +{ + bool result = true; + debugOutput(DEBUG_LEVEL_VERBOSE, "Enter from state: %s\n", ePSToString(m_state)); + switch(m_state) { + case ePS_WaitingForStream: + // a running stream has been detected + debugOutput(DEBUG_LEVEL_VERBOSE, + "StreamProcessor %p started dry-running\n", + this); + m_local_node_id = m_1394service.getLocalNodeId() & 0x3f; + if (getType() == ePT_Receive) { + // this to ensure that there is no discontinuity when starting to + // update the DLL based upon the received packets + m_data_buffer->setBufferTailTimestamp(m_last_timestamp); + } else { + // FIXME: PC=master mode will have to do something here I guess... + } + break; + case ePS_WaitingForStreamEnable: // when xrunning at startup + result &= m_data_buffer->clearBuffer(); + m_data_buffer->setTransparent(true); + break; + case ePS_WaitingForStreamDisable: + result &= m_data_buffer->clearBuffer(); + m_data_buffer->setTransparent(true); + break; + default: + debugError("Entry from invalid state: %s\n", ePSToString(m_state)); + return false; + } + m_state = ePS_DryRunning; + #ifdef DEBUG + if (getDebugLevel() >= DEBUG_LEVEL_VERBOSE) { + debugOutput(DEBUG_LEVEL_VERBOSE, "State switch complete, dumping SP info...\n"); + dumpInfo(); + } + #endif + SIGNAL_ACTIVITY_ALL; + return result; +} + +/** + * @brief Enter the ePS_WaitingForStreamEnable state + * @return true if successful, false if not + * + * @pre + * + * @post + * + */ +bool +StreamProcessor::doWaitForStreamEnable() +{ + debugOutput(DEBUG_LEVEL_VERBOSE, "Enter from state: %s\n", ePSToString(m_state)); + unsigned int ringbuffer_size_frames; + switch(m_state) { + case ePS_DryRunning: + // we have to start waiting for an incoming stream + // this basically means nothing, the state change will + // be picked up by the packet iterator + + if(!m_data_buffer->clearBuffer()) { + debugError("Could not reset data buffer\n"); + return false; + } + if (getType() == ePT_Transmit) { + ringbuffer_size_frames = m_StreamProcessorManager.getNbBuffers() * m_StreamProcessorManager.getPeriodSize(); + debugOutput(DEBUG_LEVEL_VERBOSE, "Prefill transmit SP %p with %u frames\n", this, ringbuffer_size_frames); + // prefill the buffer + if(!transferSilence(ringbuffer_size_frames)) { + debugFatal("Could not prefill transmit stream\n"); + return false; + } + } + break; + default: + debugError("Entry from invalid state: %s\n", ePSToString(m_state)); + return false; + } + m_state = ePS_WaitingForStreamEnable; + #ifdef DEBUG + if (getDebugLevel() >= DEBUG_LEVEL_VERBOSE) { + debugOutput(DEBUG_LEVEL_VERBOSE, "State switch complete, dumping SP info...\n"); + dumpInfo(); + } + #endif + SIGNAL_ACTIVITY_ALL; + return true; +} + +/** + * @brief Enter the ePS_Running state + * @return true if successful, false if not + * + * @pre + * + * @post + * + */ +bool +StreamProcessor::doRunning() +{ + bool result = true; + debugOutput(DEBUG_LEVEL_VERBOSE, "Enter from state: %s\n", ePSToString(m_state)); + switch(m_state) { + case ePS_WaitingForStreamEnable: + // a running stream has been detected + debugOutput(DEBUG_LEVEL_VERBOSE, "StreamProcessor %p started running\n", + this); + m_in_xrun = false; + m_local_node_id = m_1394service.getLocalNodeId() & 0x3f; + m_data_buffer->setTransparent(false); + break; + default: + debugError("Entry from invalid state: %s\n", ePSToString(m_state)); + return false; + } + m_state = ePS_Running; + #ifdef DEBUG + if (getDebugLevel() >= DEBUG_LEVEL_VERBOSE) { + debugOutput(DEBUG_LEVEL_VERBOSE, "State switch complete, dumping SP info...\n"); + dumpInfo(); + } + #endif + SIGNAL_ACTIVITY_ALL; + return result; +} + +/** + * @brief Enter the ePS_WaitingForStreamDisable state + * @return true if successful, false if not + * + * @pre + * + * @post + * + */ +bool +StreamProcessor::doWaitForStreamDisable() +{ + debugOutput(DEBUG_LEVEL_VERBOSE, "Enter from state: %s\n", ePSToString(m_state)); + switch(m_state) { + case ePS_Running: + // the thread will do the transition + break; + default: + debugError("Entry from invalid state: %s\n", ePSToString(m_state)); + return false; + } + m_state = ePS_WaitingForStreamDisable; + #ifdef DEBUG + if (getDebugLevel() >= DEBUG_LEVEL_VERBOSE) { + debugOutput(DEBUG_LEVEL_VERBOSE, "State switch complete, dumping SP info...\n"); + dumpInfo(); + } + #endif + SIGNAL_ACTIVITY_ALL; + return true; +} + +/** + * @brief Updates the state machine and calls the necessary transition functions + * @return true if successful, false if not + */ +bool StreamProcessor::updateState() { + bool result = false; + // copy the current state locally since it could change value, + // and that's something we don't want to happen inbetween tests + // if m_next_state changes during this routine, we know for sure + // that the previous state change was at least attempted correctly. + enum eProcessorState next_state = m_next_state; + + debugOutput(DEBUG_LEVEL_VERBOSE, "Do state transition: %s => %s\n", + ePSToString(m_state), ePSToString(next_state)); + + if (m_state == next_state) { + debugWarning("ignoring identity state update from/to %s\n", ePSToString(m_state) ); + return true; + } + // after creation, only initialization is allowed + if (m_state == ePS_Created) { + if(next_state != ePS_Stopped) { + goto updateState_exit_with_error; + } + // do init here + result = doStop(); + if (result) {return true;} + else goto updateState_exit_change_failed; + } + + // after initialization, only WaitingForRunningStream is allowed + if (m_state == ePS_Stopped) { + if(next_state != ePS_WaitingForStream) { + goto updateState_exit_with_error; + } + result = doWaitForRunningStream(); + if (result) {return true;} + else goto updateState_exit_change_failed; + } + + // after WaitingForStream, only ePS_DryRunning is allowed + // this means that the stream started running + if (m_state == ePS_WaitingForStream) { + if(next_state != ePS_DryRunning) { + goto updateState_exit_with_error; + } + result = doDryRunning(); + if (result) {return true;} + else goto updateState_exit_change_failed; + } + + // from ePS_DryRunning we can go to: + // - ePS_Stopped if something went wrong during DryRunning + // - ePS_WaitingForStreamEnable if there is a requested to enable + if (m_state == ePS_DryRunning) { + if((next_state != ePS_Stopped) && + (next_state != ePS_WaitingForStreamEnable)) { + goto updateState_exit_with_error; + } + if (next_state == ePS_Stopped) { + result = doStop(); + } else { + result = doWaitForStreamEnable(); + } + if (result) {return true;} + else goto updateState_exit_change_failed; + } + + // from ePS_WaitingForStreamEnable we can go to: + // - ePS_DryRunning if something went wrong while waiting + // - ePS_Running if the stream enabled correctly + if (m_state == ePS_WaitingForStreamEnable) { + if((next_state != ePS_DryRunning) && + (next_state != ePS_Running)) { + goto updateState_exit_with_error; + } + if (next_state == ePS_DryRunning) { + result = doDryRunning(); + } else { + result = doRunning(); + } + if (result) {return true;} + else goto updateState_exit_change_failed; + } + + // from ePS_Running we can only start waiting for a disabled stream + if (m_state == ePS_Running) { + if(next_state != ePS_WaitingForStreamDisable) { + goto updateState_exit_with_error; + } + result = doWaitForStreamDisable(); + if (result) {return true;} + else goto updateState_exit_change_failed; + } + + // from ePS_WaitingForStreamDisable we can go to DryRunning + if (m_state == ePS_WaitingForStreamDisable) { + if(next_state != ePS_DryRunning) { + goto updateState_exit_with_error; + } + result = doDryRunning(); + if (result) {return true;} + else goto updateState_exit_change_failed; + } + + // if we arrive here there is an error +updateState_exit_with_error: + debugError("Invalid state transition: %s => %s\n", + ePSToString(m_state), ePSToString(next_state)); + SIGNAL_ACTIVITY_ALL; + return false; +updateState_exit_change_failed: + debugError("State transition failed: %s => %s\n", + ePSToString(m_state), ePSToString(next_state)); + SIGNAL_ACTIVITY_ALL; + return false; +} + +bool StreamProcessor::canProducePacket() +{ + return canProduce(getNominalFramesPerPacket()); +} +bool StreamProcessor::canProducePeriod() +{ + return canProduce(m_StreamProcessorManager.getPeriodSize()); +} +bool StreamProcessor::canProduce(unsigned int nframes) +{ + if(m_in_xrun) return true; + if(m_state == ePS_Running && m_next_state == ePS_Running) { + // can we put a certain amount of frames into the buffer? + unsigned int bufferspace = m_data_buffer->getBufferSpace(); + if(bufferspace >= nframes) { + return true; + } else return false; + } else { + if(getType() == ePT_Transmit) { + // if we are an xmit SP, we cannot accept frames + // when not running + return false; + } else { + // if we are a receive SP, we can always accept frames + // when not running + return true; + } + } +} + +bool StreamProcessor::canConsumePacket() +{ + return canConsume(getNominalFramesPerPacket()); +} +bool StreamProcessor::canConsumePeriod() +{ + return canConsume(m_StreamProcessorManager.getPeriodSize()); +} +bool StreamProcessor::canConsume(unsigned int nframes) +{ + if(m_in_xrun) return true; + if(m_state == ePS_Running && m_next_state == ePS_Running) { + // check whether we already fullfil the criterion + unsigned int bufferfill = m_data_buffer->getBufferFill(); + if(bufferfill >= nframes) { + return true; + } else return false; + } else { + if(getType() == ePT_Transmit) { + // if we are an xmit SP, and we're not running, + // we can always provide frames + return true; + } else { + // if we are a receive SP, we can't provide frames + // when not running + return false; + } + } +} + +/*********************************************** + * Helper routines * + ***********************************************/ +// FIXME: I think this can be removed and replaced by putSilenceFrames +bool +StreamProcessor::transferSilence(unsigned int nframes) +{ + bool retval; + + #ifdef DEBUG + signed int fc; + ffado_timestamp_t ts_tail_tmp; + m_data_buffer->getBufferTailTimestamp(&ts_tail_tmp, &fc); + if (fc != 0) { + debugWarning("Prefilling a buffer that already contains %d frames\n", fc); + } + #endif + + // prepare a buffer of silence + char *dummybuffer = (char *)calloc(getEventSize(), nframes * getEventsPerFrame()); + transmitSilenceBlock(dummybuffer, nframes, 0); + + // add the silence data to the ringbuffer + if(m_data_buffer->preloadFrames(nframes, dummybuffer, true)) { + retval = true; + } else { + debugWarning("Could not write to event buffer\n"); + retval = false; + } + + free(dummybuffer); + return retval; +} + +/** + * @brief convert a eProcessorState to a string + * @param s the state + * @return a char * describing the state + */ +const char * +StreamProcessor::ePSToString(enum eProcessorState s) { + switch (s) { + case ePS_Invalid: return "ePS_Invalid"; + case ePS_Created: return "ePS_Created"; + case ePS_Stopped: return "ePS_Stopped"; + case ePS_WaitingForStream: return "ePS_WaitingForStream"; + case ePS_DryRunning: return "ePS_DryRunning"; + case ePS_WaitingForStreamEnable: return "ePS_WaitingForStreamEnable"; + case ePS_Running: return "ePS_Running"; + case ePS_WaitingForStreamDisable: return "ePS_WaitingForStreamDisable"; + default: return "error: unknown state"; + } +} + +/** + * @brief convert a eProcessorType to a string + * @param t the type + * @return a char * describing the state + */ +const char * +StreamProcessor::ePTToString(enum eProcessorType t) { + switch (t) { + case ePT_Receive: return "Receive"; + case ePT_Transmit: return "Transmit"; + default: return "error: unknown type"; + } +} + +/*********************************************** + * Debug * + ***********************************************/ +void +StreamProcessor::dumpInfo() +{ + #ifdef DEBUG + debugOutputShort( DEBUG_LEVEL_NORMAL, " StreamProcessor %p, %s:\n", this, ePTToString(m_processor_type)); + debugOutputShort( DEBUG_LEVEL_NORMAL, " Port, Channel : %d, %d\n", m_1394service.getPort(), m_channel); + uint64_t now = m_1394service.getCycleTimerTicks(); + debugOutputShort( DEBUG_LEVEL_NORMAL, " Now : %011llu (%03us %04uc %04ut)\n", + now, + (unsigned int)TICKS_TO_SECS(now), + (unsigned int)TICKS_TO_CYCLES(now), + (unsigned int)TICKS_TO_OFFSET(now)); + debugOutputShort( DEBUG_LEVEL_NORMAL, " Xrun? : %s\n", (m_in_xrun ? "True":"False")); + if (m_state == m_next_state) { + debugOutputShort( DEBUG_LEVEL_NORMAL, " State : %s\n", + ePSToString(m_state)); + } else { + debugOutputShort( DEBUG_LEVEL_NORMAL, " State : %s (Next: %s)\n", + ePSToString(m_state), ePSToString(m_next_state)); + debugOutputShort( DEBUG_LEVEL_NORMAL, " transition at : %u\n", m_cycle_to_switch_state); + } + debugOutputShort( DEBUG_LEVEL_NORMAL, " Buffer : %p\n", m_data_buffer); + debugOutputShort( DEBUG_LEVEL_NORMAL, " Framerate : Nominal: %u, Sync: %f, Buffer %f\n", + m_StreamProcessorManager.getNominalRate(), + 24576000.0/m_StreamProcessorManager.getSyncSource().m_data_buffer->getRate(), + 24576000.0/m_data_buffer->getRate()); + float d = getSyncDelay(); + debugOutputShort(DEBUG_LEVEL_NORMAL, " Sync delay : %f ticks (%f frames, %f cy)\n", + d, d/getTicksPerFrame(), + d/((float)TICKS_PER_CYCLE)); + #endif + m_data_buffer->dumpInfo(); +} + +void +StreamProcessor::printBufferInfo() +{ + debugOutput(DEBUG_LEVEL_NORMAL, + "(%p, %8s) fc: %d fill: %u\n", + this, getTypeString(), m_data_buffer->getFrameCounter(), m_data_buffer->getBufferFill() ); +} + +void +StreamProcessor::setVerboseLevel(int l) { + setDebugLevel(l); + PortManager::setVerboseLevel(l); + m_data_buffer->setVerboseLevel(l); +} + +} // end of namespace Index: /branches/libffado-2.0/src/libstreaming/util/cip.h =================================================================== --- /branches/libffado-2.0/src/libstreaming/util/cip.h (revision 1138) +++ /branches/libffado-2.0/src/libstreaming/util/cip.h (revision 1138) @@ -0,0 +1,177 @@ +/* + * libiec61883 - Linux IEEE 1394 streaming media library. + * Copyright (C) 2004 Kristian Hogsberg, Dan Dennedy, and Dan Maas. + * This file written by Kristian Hogsberg. + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef _IEC61883_CIP_PRIVATE_H +#define _IEC61883_CIP_PRIVATE_H + +#include +#include +#include + +#define IEC61883_FMT_DV 0x00 +#define IEC61883_FMT_AMDTP 0x10 +#define IEC61883_FMT_MPEG2 0x20 + +#define CIP_TRANSFER_DELAY 9000 + + +#ifdef __cplusplus +extern "C" { +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN + + +struct iec61883_packet { + /* First quadlet */ + uint8_t eoh0 : 2; + uint8_t sid : 6; + uint8_t dbs : 8; + + uint8_t fn : 2; + uint8_t qpc : 3; + uint8_t sph : 1; + uint8_t reserved : 2; + uint8_t dbc : 8; + + /* Second quadlet */ + uint8_t eoh1 : 2; + uint8_t fmt : 6; + uint8_t fdf : 8; + + uint16_t syt : 16; + + unsigned char data[0]; +}; + +#elif __BYTE_ORDER == __LITTLE_ENDIAN + +struct iec61883_packet { + /* First quadlet */ + uint8_t sid : 6; + uint8_t eoh0 : 2; + uint8_t dbs : 8; + + uint8_t reserved : 2; + uint8_t sph : 1; + uint8_t qpc : 3; + uint8_t fn : 2; + uint8_t dbc : 8; + + /* Second quadlet */ + uint8_t fmt : 6; + uint8_t eoh1 : 2; + uint8_t fdf : 8; + + uint16_t syt : 16; + + unsigned char data[0]; +}; + +#else + +#error Unknown bitfield type + +#endif + +/* + * The TAG value is present in the isochronous header (first quadlet). It + * provides a high level label for the format of data carried by the + * isochronous packet. + */ + +#define IEC61883_TAG_WITHOUT_CIP 0 /* CIP header NOT included */ +#define IEC61883_TAG_WITH_CIP 1 /* CIP header included. */ +#define IEC61883_TAG_RESERVED1 2 /* Reserved */ +#define IEC61883_TAG_RESERVED2 3 /* Reserved */ + +#define IEC61883_FDF_NODATA 0xFF + +/* AM824 format definitions. */ +#define IEC61883_FDF_AM824 0x00 +#define IEC61883_FDF_AM824_CONTROLLED 0x04 +#define IEC61883_FDF_SFC_MASK 0x03 + +#define IEC61883_AM824_LABEL 0x40 +#define IEC61883_AM824_LABEL_RAW_24BITS 0x40 +#define IEC61883_AM824_LABEL_RAW_20BITS 0x41 +#define IEC61883_AM824_LABEL_RAW_16BITS 0x42 +#define IEC61883_AM824_LABEL_RAW_RESERVED 0x43 + +#define IEC61883_AM824_VBL_24BITS 0x0 +#define IEC61883_AM824_VBL_20BITS 0x1 +#define IEC61883_AM824_VBL_16BITS 0x2 +#define IEC61883_AM824_VBL_RESERVED 0x3 + +/* IEC-60958 format definitions. */ +#define IEC60958_LABEL 0x0 +#define IEC60958_PAC_B 0x3 /* Preamble Code 'B': Start of channel 1, at + * the start of a data block. */ +#define IEC60958_PAC_RSV 0x2 /* Preamble Code 'RESERVED' */ +#define IEC60958_PAC_M 0x1 /* Preamble Code 'M': Start of channel 1 that + * is not at the start of a data block. */ +#define IEC60958_PAC_W 0x0 /* Preamble Code 'W': start of channel 2. */ +#define IEC60958_DATA_VALID 0 /* When cleared means data is valid. */ +#define IEC60958_DATA_INVALID 1 /* When set means data is not suitable for an ADC. */ + +struct iec61883_fraction { + int integer; + int numerator; + int denominator; +}; + +struct iec61883_cip { + struct iec61883_fraction cycle_offset; + struct iec61883_fraction ticks_per_syt_offset; + struct iec61883_fraction ready_samples; + struct iec61883_fraction samples_per_cycle; + int dbc, dbs; + int cycle_count; + int cycle_count2; + int mode; + int syt_interval; + int dimension; + int rate; + int fdf; + int format; +}; + +void +iec61883_cip_init(struct iec61883_cip *cip, int format, int fdf, + int rate, int dbs, int syt_interval); +void +iec61883_cip_set_transmission_mode(struct iec61883_cip *ptz, int mode); + +int +iec61883_cip_get_max_packet_size(struct iec61883_cip *ptz); + +int +iec61883_cip_fill_header(int node_id, struct iec61883_cip *cip, + struct iec61883_packet *packet); + +int +iec61883_cip_fill_header_nodata(int node_id, struct iec61883_cip *cip, + struct iec61883_packet *packet); + +#ifdef __cplusplus +} +#endif + +#endif Index: /branches/libffado-2.0/src/libstreaming/util/cip.c =================================================================== --- /branches/libffado-2.0/src/libstreaming/util/cip.c (revision 864) +++ /branches/libffado-2.0/src/libstreaming/util/cip.c (revision 864) @@ -0,0 +1,236 @@ +/* + * libiec61883 - Linux IEEE 1394 streaming media library. + * Copyright (C) 2004 Kristian Hogsberg, Dan Dennedy, and Dan Maas. + * This file written by Kristian Hogsberg. + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "cip.h" + +#include +#include + + +/* Integer fractional math. When we transmit a 44k1Hz signal we must + * send 5 41/80 samples per isochronous cycle, as these occur 8000 + * times a second. Of course, we must send an integral number of + * samples in a packet, so we use the integer math to alternate + * between sending 5 and 6 samples per packet. + */ + +static void +fraction_init(struct iec61883_fraction *f, int numerator, int denominator) +{ + f->integer = numerator / denominator; + f->numerator = numerator % denominator; + f->denominator = denominator; +} + +static __inline__ void +fraction_add(struct iec61883_fraction *dst, + struct iec61883_fraction *src1, struct iec61883_fraction *src2) +{ + /* assert: src1->denominator == src2->denominator */ + + int sum, denom; + + /* We use these two local variables to allow gcc to optimize + * the division and the modulo into only one division. */ + + sum = src1->numerator + src2->numerator; + denom = src1->denominator; + dst->integer = src1->integer + src2->integer + sum / denom; + dst->numerator = sum % denom; + dst->denominator = denom; +} + +static __inline__ void +fraction_sub_int(struct iec61883_fraction *dst, struct iec61883_fraction *src, int integer) +{ + dst->integer = src->integer - integer; + dst->numerator = src->numerator; + dst->denominator = src->denominator; +} + +static __inline__ int +fraction_floor(struct iec61883_fraction *frac) +{ + return frac->integer; +} + +static __inline__ int +fraction_ceil(struct iec61883_fraction *frac) +{ + return frac->integer + (frac->numerator > 0 ? 1 : 0); +} + +void +iec61883_cip_init(struct iec61883_cip *ptz, int format, int fdf, + int rate, int dbs, int syt_interval) +{ + const int transfer_delay = CIP_TRANSFER_DELAY; + + ptz->rate = rate; + ptz->cycle_count = transfer_delay / 3072; + ptz->cycle_count2 = 0; + ptz->format = format; + ptz->fdf = fdf; + ptz->mode = IEC61883_MODE_BLOCKING_EMPTY; + ptz->dbs = dbs; + ptz->dbc = 0; + ptz->syt_interval = syt_interval; + + fraction_init(&ptz->samples_per_cycle, ptz->rate, 8000); + fraction_init(&ptz->ready_samples, 0, 8000); + + /* The ticks_per_syt_offset is initialized to the number of + * ticks between syt_interval events. The number of ticks per + * second is 24.576e6, so the number of ticks between + * syt_interval events is 24.576e6 * syt_interval / rate. + */ + fraction_init(&ptz->ticks_per_syt_offset, + 24576000 * ptz->syt_interval, ptz->rate); + fraction_init(&ptz->cycle_offset, + (transfer_delay % 3072) * ptz->rate, ptz->rate); +} + +void +iec61883_cip_set_transmission_mode(struct iec61883_cip *ptz, int mode) +{ + ptz->mode = mode; +} + +int +iec61883_cip_get_max_packet_size(struct iec61883_cip *ptz) +{ + int max_nevents; + + if (ptz->mode == IEC61883_MODE_BLOCKING_EMPTY || ptz->mode == IEC61883_MODE_BLOCKING_NODATA) + max_nevents = ptz->syt_interval; + else + max_nevents = fraction_ceil(&ptz->samples_per_cycle); + + return max_nevents * ptz->dbs * 4 + 8; +} + + +int +iec61883_cip_fill_header(int node_id, struct iec61883_cip *ptz, + struct iec61883_packet *packet) +{ + struct iec61883_fraction next; + int nevents, nevents_dbc, syt_index, syt; + + fraction_add(&next, &ptz->ready_samples, &ptz->samples_per_cycle); + if (ptz->mode == IEC61883_MODE_BLOCKING_EMPTY || + ptz->mode == IEC61883_MODE_BLOCKING_NODATA) { + if (fraction_floor(&next) >= ptz->syt_interval) + nevents = ptz->syt_interval; + else + nevents = 0; + } + else + nevents = fraction_floor(&next); + + if (ptz->mode == IEC61883_MODE_BLOCKING_NODATA) { + /* The DBC is incremented even with NO_DATA packets. */ + nevents_dbc = ptz->syt_interval; + } + else { + nevents_dbc = nevents; + } + + /* Now that we know how many events to put in the packet, update the + * fraction ready_samples. */ + fraction_sub_int(&ptz->ready_samples, &next, nevents); + + /* Calculate synchronization timestamp (syt). First we + * determine syt_index, that is, the index in the packet of + * the sample for which the timestamp is valid. */ + syt_index = (ptz->syt_interval - ptz->dbc) & (ptz->syt_interval - 1); + if (syt_index < nevents) { + syt = ((ptz->cycle_count << 12) | fraction_floor(&ptz->cycle_offset)) & 0xffff; + fraction_add(&ptz->cycle_offset, &ptz->cycle_offset, + &ptz->ticks_per_syt_offset); + + /* This next addition should be modulo 8000 (0x1f40), + * but we only use the lower 4 bits of cycle_count, so + * we don't need the modulo. */ + ptz->cycle_count += ptz->cycle_offset.integer / 3072; + ptz->cycle_offset.integer %= 3072; + } + else + syt = 0xffff; + + packet->eoh0 = 0; + + /* Our node ID can change after a bus reset, so it is best to fetch + * our node ID for each packet. */ + packet->sid = node_id & 0x3f; + + packet->dbs = ptz->dbs; + packet->fn = 0; + packet->qpc = 0; + packet->sph = 0; + packet->reserved = 0; + packet->dbc = ptz->dbc; + packet->eoh1 = 2; + packet->fmt = ptz->format; + + if ( nevents == 0 && ptz->mode == IEC61883_MODE_BLOCKING_NODATA ) { + /* FDF code for packets containing dummy data. */ + packet->fdf = IEC61883_FDF_NODATA; + } + else { + /* FDF code for non-blocking mode and for blocking mode with empty packets. */ + packet->fdf = ptz->fdf; + } + + packet->syt = htons(syt); + + ptz->dbc += nevents_dbc; + + return nevents; +} + +// note that we don't implement timestamp increase for nodata +// FIXME: check if this is standards compliant!! +int +iec61883_cip_fill_header_nodata(int node_id, struct iec61883_cip *ptz, + struct iec61883_packet *packet) +{ + packet->eoh0 = 0; + + /* Our node ID can change after a bus reset, so it is best to fetch + * our node ID for each packet. */ + packet->sid = node_id & 0x3f; + + packet->dbs = ptz->dbs; + packet->fn = 0; + packet->qpc = 0; + packet->sph = 0; + packet->reserved = 0; + packet->dbc = ptz->dbc; + packet->eoh1 = 2; + packet->fmt = ptz->format; + + packet->fdf = IEC61883_FDF_NODATA; + packet->syt = 0xffff; + + ptz->dbc += ptz->syt_interval; + + return 0; +} Index: /branches/libffado-2.0/src/rme/rme_avdevice.h =================================================================== --- /branches/libffado-2.0/src/rme/rme_avdevice.h (revision 981) +++ /branches/libffado-2.0/src/rme/rme_avdevice.h (revision 981) @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2005-2008 by Jonathan Woithe + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef RMEDEVICE_H +#define RMEDEVICE_H + +#include "ffadodevice.h" + +#include "debugmodule/debugmodule.h" +#include "libavc/avc_definitions.h" + +// #include "libstreaming/rme/RmeStreamProcessor.h" + +/* RME Fireface register definitions */ +#define RME_REG_DDS_CONTROL 0xfc88f000 + +class ConfigRom; +class Ieee1394Service; + +namespace Rme { + +// Note: the values in this enum do not have to correspond to the unit +// version reported by the respective devices. It just so happens that they +// currently do for the Fireface-800 and Fireface-400. +enum ERmeModel { + RME_MODEL_NONE = 0x0000, + RME_MODEL_FIREFACE800 = 0x0001, + RME_MODEL_FIREFACE400 = 0x0002, +}; + +// struct to define the supported devices +struct VendorModelEntry { + unsigned int vendor_id; + unsigned int unit_version; + enum ERmeModel model; + char *vendor_name; + char *model_name; +}; + +class RmeDevice : public FFADODevice { +public: + + RmeDevice( DeviceManager& d, + std::auto_ptr( configRom )); + virtual ~RmeDevice(); + + static bool probe( ConfigRom& configRom ); + static FFADODevice * createDevice( DeviceManager& d, + std::auto_ptr( configRom )); + static int getConfigurationId( ); + virtual bool discover(); + + virtual void showDevice(); + + virtual bool setSamplingFrequency( int samplingFrequency ); + virtual int getSamplingFrequency( ); + + virtual ClockSourceVector getSupportedClockSources(); + virtual bool setActiveClockSource(ClockSource); + virtual ClockSource getActiveClockSource(); + + virtual int getStreamCount(); + virtual Streaming::StreamProcessor *getStreamProcessorByIndex(int i); + + virtual bool prepare(); + virtual bool lock(); + virtual bool unlock(); + + virtual bool startStreamByIndex(int i); + virtual bool stopStreamByIndex(int i); + + unsigned int readRegister(unsigned int reg); + signed int writeRegister(unsigned int reg, quadlet_t data); + +protected: + struct VendorModelEntry *m_model; + enum ERmeModel m_rme_model; + + signed int m_ddsFreq; +}; + +} + +#endif Index: /branches/libffado-2.0/src/rme/rme_avdevice.cpp =================================================================== --- /branches/libffado-2.0/src/rme/rme_avdevice.cpp (revision 1136) +++ /branches/libffado-2.0/src/rme/rme_avdevice.cpp (revision 1136) @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2005-2008 by Jonathan Woithe + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#warning RME support is currently useless (detection only) + +#include "rme/rme_avdevice.h" + +#include "libieee1394/configrom.h" +#include "libieee1394/ieee1394service.h" + +#include "debugmodule/debugmodule.h" + +#include +#include +#include +#include "libutil/ByteSwap.h" + +#include +#include + +#include + +namespace Rme { + +// to define the supported devices +static VendorModelEntry supportedDeviceList[] = +{ + {FW_VENDORID_RME, 0x0001, RME_MODEL_FIREFACE800, "RME", "Fireface-800"}, + {FW_VENDORID_RME, 0x0002, RME_MODEL_FIREFACE400, "RME", "Fireface-400"}, +}; + +RmeDevice::RmeDevice( DeviceManager& d, + std::auto_ptr( configRom )) + : FFADODevice( d, configRom ) + , m_model( NULL ) + , m_rme_model( RME_MODEL_NONE ) + , m_ddsFreq( -1 ) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Created Rme::RmeDevice (NodeID %d)\n", + getConfigRom().getNodeId() ); +} + +RmeDevice::~RmeDevice() +{ + +} + +bool +RmeDevice::probe( ConfigRom& configRom ) +{ + unsigned int vendorId = configRom.getNodeVendorId(); + unsigned int unitVersion = configRom.getUnitVersion(); + + for ( unsigned int i = 0; + i < ( sizeof( supportedDeviceList )/sizeof( VendorModelEntry ) ); + ++i ) + { + if ( ( supportedDeviceList[i].vendor_id == vendorId ) + && ( supportedDeviceList[i].unit_version == unitVersion ) + ) + { + return true; + } + } + + return false; +} + +FFADODevice * +RmeDevice::createDevice(DeviceManager& d, std::auto_ptr( configRom )) +{ + return new RmeDevice(d, configRom ); +} + +bool +RmeDevice::discover() +{ + unsigned int vendorId = getConfigRom().getNodeVendorId(); + unsigned int unitVersion = getConfigRom().getUnitVersion(); + + for ( unsigned int i = 0; + i < ( sizeof( supportedDeviceList )/sizeof( VendorModelEntry ) ); + ++i ) + { + if ( ( supportedDeviceList[i].vendor_id == vendorId ) + && ( supportedDeviceList[i].unit_version == unitVersion ) + ) + { + m_model = &(supportedDeviceList[i]); + m_rme_model = supportedDeviceList[i].model; + } + } + + if (m_model != NULL) { + debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n", + m_model->vendor_name, m_model->model_name); + return true; + } + + return false; +} + +int +RmeDevice::getSamplingFrequency( ) { +/* + * Retrieve the current sample rate from the RME device. At this stage it + * seems that the "current rate" can't be retrieved from the device. Other + * drivers don't read the DDS control register and there isn't anywhere else + * where the frequency is sent back to the PC. Unless we test the DDS + * control register for readabilty and find it can be read we'll assume it + * can't and instead cache the DDS frequency. + * + * If the device frequency has not been set this function will return -1 + * (the default value of m_ddsFreq). + */ + return m_ddsFreq; +} + +int +RmeDevice::getConfigurationId() +{ + return 0; +} + +bool +RmeDevice::setSamplingFrequency( int samplingFrequency ) +{ +/* + * Set the RME device's samplerate. The RME can do sampling frequencies of + * 32k, 44.1k and 48k along with the corresponding 2x and 4x rates. + * However, it can also do +/- 4% from any of these "base" frequencies. + * This makes it a little long-winded to work out whether a given frequency + * is supported or not. + */ + + /* Work out whether the requested rate is supported */ + if (!((samplingFrequency >= 32000*0.96 && samplingFrequency <= 32000*1.04) || + (samplingFrequency >= 44100*0.96 && samplingFrequency <= 44100*1.04) || + (samplingFrequency >= 48000*0.96 && samplingFrequency <= 48000*1.04) || + (samplingFrequency >= 64000*0.96 && samplingFrequency <= 64000*1.04) || + (samplingFrequency >= 88200*0.96 && samplingFrequency <= 88200*1.04) || + (samplingFrequency >= 96000*0.96 && samplingFrequency <= 96000*1.04) || + (samplingFrequency >= 128000*0.96 && samplingFrequency <= 128000*1.04) || + (samplingFrequency >= 176000*0.96 && samplingFrequency <= 176000*1.04) || + (samplingFrequency >= 192000*0.96 && samplingFrequency <= 192000*1.04))) { + return false; + } + + /* Send the desired frequency to the RME */ + if (writeRegister(RME_REG_DDS_CONTROL, samplingFrequency) != 0) + return false; + + m_ddsFreq = samplingFrequency; + return true; +} + +FFADODevice::ClockSourceVector +RmeDevice::getSupportedClockSources() { + FFADODevice::ClockSourceVector r; + return r; +} + +bool +RmeDevice::setActiveClockSource(ClockSource s) { + return false; +} + +FFADODevice::ClockSource +RmeDevice::getActiveClockSource() { + ClockSource s; + return s; +} + +bool +RmeDevice::lock() { + + return true; +} + + +bool +RmeDevice::unlock() { + + return true; +} + +void +RmeDevice::showDevice() +{ + debugOutput(DEBUG_LEVEL_VERBOSE, + "%s %s at node %d\n", m_model->vendor_name, m_model->model_name, + getNodeId()); +} + +bool +RmeDevice::prepare() { + + debugOutput(DEBUG_LEVEL_NORMAL, "Preparing RmeDevice...\n" ); + + return true; +} + +int +RmeDevice::getStreamCount() { + return 0; // one receive, one transmit +} + +Streaming::StreamProcessor * +RmeDevice::getStreamProcessorByIndex(int i) { + return NULL; +} + +bool +RmeDevice::startStreamByIndex(int i) { + return false; +} + +bool +RmeDevice::stopStreamByIndex(int i) { + return false; + +} + +unsigned int +RmeDevice::readRegister(unsigned int reg) { + + quadlet_t quadlet; + + quadlet = 0; + if (get1394Service().read(0xffc0 | getNodeId(), reg, 1, &quadlet) <= 0) { + debugError("Error doing RME read from register 0x%06x\n",reg); + } + return CondSwapFromBus32(quadlet); +} + +signed int +RmeDevice::writeRegister(unsigned int reg, quadlet_t data) { + + unsigned int err = 0; + data = CondSwapToBus32(data); + if (get1394Service().write(0xffc0 | getNodeId(), reg, 1, &data) <= 0) { + err = 1; + debugError("Error doing RME write to register 0x%06x\n",reg); + } +// SleepRelativeUsec(100); + return (err==0)?0:-1; +} + +} Index: /branches/libffado-2.0/src/DeviceStringParser.cpp =================================================================== --- /branches/libffado-2.0/src/DeviceStringParser.cpp (revision 1147) +++ /branches/libffado-2.0/src/DeviceStringParser.cpp (revision 1147) @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "DeviceStringParser.h" + +#include +#include + +#include +#include + +#include "libieee1394/configrom.h" +#include "libieee1394/ieee1394service.h" + +IMPL_DEBUG_MODULE( DeviceStringParser, DeviceStringParser, DEBUG_LEVEL_NORMAL ); + +DeviceStringParser::DeviceString::DeviceString(DeviceStringParser& parent) + : m_Parent(parent) + , m_node( -1 ) + , m_port( -1 ) + , m_guid( 0 ) + , m_String("") + , m_Type(eInvalid) + , m_debugModule( parent.m_debugModule ) +{ +} + +DeviceStringParser::DeviceString::~DeviceString() +{ +} + +bool +DeviceStringParser::DeviceString::parse(std::string s) +{ + m_String = s; + debugOutput(DEBUG_LEVEL_VERBOSE, "parse: %s\n", s.c_str()); + std::string prefix = s.substr(0,3); + + if(s.compare(0,3,"hw:")==0) { + m_Type = eBusNode; + std::string detail = s.substr(3); + std::string::size_type comma_pos = detail.find_first_of(","); + if(comma_pos == std::string::npos) { + // node is unspecified + m_node = -1; + std::string port = detail; + errno = 0; + m_port = strtol(port.c_str(), NULL, 0); + if(errno) { + m_Type = eInvalid; + m_port = -1; + m_node = -1; + debugOutput(DEBUG_LEVEL_VERBOSE, "failed to parse port\n"); + return false; + } + } else { + std::string port = detail.substr(0, comma_pos); + std::string node = detail.substr(comma_pos+1); + errno = 0; + m_port = strtol(port.c_str(), NULL, 0); + if(errno) { + m_Type = eInvalid; + m_port = -1; + m_node = -1; + debugOutput(DEBUG_LEVEL_VERBOSE, "failed to parse port\n"); + return false; + } + errno = 0; + m_node = strtol(node.c_str(), NULL, 0); + if(errno) { + m_Type = eInvalid; + m_port = -1; + m_node = -1; + debugOutput(DEBUG_LEVEL_VERBOSE, "failed to parse node\n"); + return false; + } + } + } else if (s.compare(0,5,"guid:")==0) { + std::string detail = s.substr(5); + m_Type = eGUID; + errno = 0; + m_guid = strtoll(detail.c_str(), NULL, 0); + if(errno) { + m_Type = eInvalid; + m_guid = 0; + debugOutput(DEBUG_LEVEL_VERBOSE, "failed to parse guid\n"); + return false; + } + } else { + m_Type = eInvalid; + debugOutput(DEBUG_LEVEL_VERBOSE, "invalid\n"); + return false; + } + return true; +} + +bool +DeviceStringParser::DeviceString::isValidString(std::string s) +{ + std::string prefix = s.substr(0,3); + uint64_t tmp; + if(s.compare(0,3,"hw:")==0) { + std::string detail = s.substr(3); + std::string::size_type comma_pos = detail.find_first_of(","); + if(comma_pos == std::string::npos) { + std::string port = detail; + errno = 0; + tmp = strtol(port.c_str(), NULL, 0); + if(errno) { + return false; + } + } else { + std::string port = detail.substr(0, comma_pos); + std::string node = detail.substr(comma_pos+1); + errno = 0; + tmp = strtol(port.c_str(), NULL, 0); + if(errno) { + return false; + } + errno = 0; + tmp = strtol(node.c_str(), NULL, 0); + if(errno) { + return false; + } + } + } else if (s.compare(0,5,"guid:")==0) { + std::string detail = s.substr(5); + errno = 0; + tmp = strtoll(detail.c_str(), NULL, 0); + if(errno) { + return false; + } + } else { + return false; + } + return true; +} + +bool +DeviceStringParser::DeviceString::match(ConfigRom& configRom) +{ + debugOutput(DEBUG_LEVEL_VERBOSE, "match %p\n", &configRom); + bool match; + switch(m_Type) { + case eBusNode: + if(m_port < 0) { + debugWarning("Need at least a port spec\n"); + return false; + } + match = configRom.get1394Service().getPort() == m_port; + if(m_node >= 0) { + match &= ((configRom.getNodeId() & 0x3F) == m_node); + } + return match; + case eGUID: + //GUID should not be 0 + return m_guid && (m_guid == configRom.getGuid()); + case eInvalid: + default: + debugOutput(DEBUG_LEVEL_VERBOSE, "no match %p\n", &configRom); + return false; + } + return false; +} + +bool +DeviceStringParser::DeviceString::operator==(const DeviceString& x) +{ + bool retval; + switch(m_Type) { + case eBusNode: + retval = (m_port == x.m_port) && (m_node == x.m_node); + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "eBusNode %d,%d == %d,%d? %d\n", + m_port, m_node, x.m_port, x.m_node, retval); + return retval; + case eGUID: + retval = m_guid && (m_guid == x.m_guid); + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "eGUID 0x%016X == 0x%016X? %d\n", + m_guid, x.m_guid, retval); + return retval; + case eInvalid: + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "eInvalid \n"); + default: + return false; + } +} + +void +DeviceStringParser::DeviceString::show() +{ + debugOutput(DEBUG_LEVEL_INFO, "string: %s\n", m_String.c_str()); + switch(m_Type) { + case eBusNode: + debugOutput(DEBUG_LEVEL_INFO, "type: eBusNode\n"); + debugOutput(DEBUG_LEVEL_INFO, " Port: %d, Node: %d\n", + m_port, m_node); + break; + case eGUID: + debugOutput(DEBUG_LEVEL_INFO, "type: eGUID\n"); + debugOutput(DEBUG_LEVEL_INFO, " GUID: %016LLX\n", m_guid); + break; + case eInvalid: + default: + debugOutput(DEBUG_LEVEL_INFO, "type: eInvalid\n"); + break; + } +} + +// ------------------------ +DeviceStringParser::DeviceStringParser() +{} + +DeviceStringParser::~DeviceStringParser() +{ + while(m_DeviceStrings.size()) { + DeviceString *tmp = m_DeviceStrings.at(0); + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "removing device string: %p\n", tmp); + m_DeviceStrings.erase(m_DeviceStrings.begin()); + delete tmp; + } +} + +bool +DeviceStringParser::parseString(std::string s) +{ + debugOutput(DEBUG_LEVEL_VERBOSE, "parse: %s\n", s.c_str()); + + std::string::size_type next_sep; + std::string tmp = s; + do { + debugOutput(DEBUG_LEVEL_VERBOSE, " left: %s\n", tmp.c_str()); + next_sep = tmp.find_first_of(";"); + std::string to_parse = tmp.substr(0, next_sep); + DeviceString *d = new DeviceString(*this); + if(d == NULL) { + debugError("failed to allocate memory for device string\n"); + continue; + } + if(d->parse(to_parse)) { + addDeviceString(d); + } else { + debugWarning("Failed to parse device substring: \"%s\"\n", + to_parse.c_str()); + delete d; + } + tmp = tmp.substr(next_sep+1); + } while(tmp.size() && next_sep != std::string::npos); + + pruneDuplicates(); + + return true; +} + +bool +DeviceStringParser::isValidString(std::string s) +{ + debugOutput(DEBUG_LEVEL_VERBOSE, "isvalid? %s\n", s.c_str()); + return DeviceString::isValidString(s); +} + +bool +DeviceStringParser::match(ConfigRom& c) +{ + for ( DeviceStringVectorIterator it = m_DeviceStrings.begin(); + it != m_DeviceStrings.end(); + ++it ) + { + if((*it)->match(c)) { + return true; + } + } + return false; +} +bool +DeviceStringParser::addDeviceString(DeviceString *o) +{ + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "adding device string: %p\n", o); + if (hasDeviceString(o)){ + return false; + } + m_DeviceStrings.push_back(o); + return true; +} + +bool +DeviceStringParser::removeDeviceString(DeviceString *o) +{ + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "removing device string: %p\n", o); + int i=findDeviceString(o); + if (i<0) { + debugOutput(DEBUG_LEVEL_VERBOSE, "not found\n"); + return false; + } else { + DeviceString *tmp = m_DeviceStrings.at(i); + m_DeviceStrings.erase(m_DeviceStrings.begin()+i); + delete tmp; + return true; + } +} + +bool +DeviceStringParser::hasDeviceString(DeviceString *o) +{ + return (findDeviceString(o) >= 0); +} + +int +DeviceStringParser::findDeviceString(DeviceString *o) +{ + int i=0; + for ( DeviceStringVectorIterator it = m_DeviceStrings.begin(); + it != m_DeviceStrings.end(); + ++it ) + { + if(*it == o) { + return i; + } + i++; + } + return -1; +} + +void +DeviceStringParser::pruneDuplicates() +{ + DeviceStringVector duplicates; + // find duplicates + for ( DeviceStringVectorIterator it = m_DeviceStrings.begin(); + it != m_DeviceStrings.end(); + ++it ) + { + for ( DeviceStringVectorIterator it2 = it+1; + it2 != m_DeviceStrings.end(); + ++it2 ) + { + + if(**it == **it2) { + duplicates.push_back(*it2); + } + } + } + + // remove duplicates + for ( DeviceStringVectorIterator it = duplicates.begin(); + it != duplicates.end(); + ++it ) + { + removeDeviceString(*it); + } +} + + +void +DeviceStringParser::show() +{ + debugOutput(DEBUG_LEVEL_INFO, "DeviceStringParser: %p\n", this); + for ( DeviceStringVectorIterator it = m_DeviceStrings.begin(); + it != m_DeviceStrings.end(); + ++it ) + { + (*it)->show(); + } +} + +void +DeviceStringParser::setVerboseLevel(int i) +{ + setDebugLevel(i); +} Index: /branches/libffado-2.0/src/motu/motu_avdevice.h =================================================================== --- /branches/libffado-2.0/src/motu/motu_avdevice.h (revision 1128) +++ /branches/libffado-2.0/src/motu/motu_avdevice.h (revision 1128) @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * Copyright (C) 2005-2008 by Jonathan Woithe + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef MOTUDEVICE_H +#define MOTUDEVICE_H + +#include "ffadodevice.h" + +#include "debugmodule/debugmodule.h" +#include "libavc/avc_definitions.h" + +#include "libstreaming/motu/MotuReceiveStreamProcessor.h" +#include "libstreaming/motu/MotuTransmitStreamProcessor.h" + +#include "motu_controls.h" + +#define MOTU_BASE_ADDR 0xfffff0000000ULL + +#define MOTU_RATE_BASE_44100 (0<<3) +#define MOTU_RATE_BASE_48000 (1<<3) +#define MOTU_RATE_MULTIPLIER_1X (0<<4) +#define MOTU_RATE_MULTIPLIER_2X (1<<4) +#define MOTU_RATE_MULTIPLIER_4X (2<<4) +#define MOTU_RATE_BASE_MASK (0x00000008) +#define MOTU_RATE_MULTIPLIER_MASK (0x00000030) + +#define MOTU_OPTICAL_MODE_OFF 0x00 +#define MOTU_OPTICAL_MODE_ADAT 0x01 +#define MOTU_OPTICAL_MODE_TOSLINK 0x02 +#define MOTU_OPTICAL_IN_MODE_MASK (0x00000300) +#define MOTU_OPTICAL_OUT_MODE_MASK (0x00000c00) +#define MOTU_OPTICAL_MODE_MASK (MOTU_OPTICAL_IN_MODE_MASK|MOTU_OPTICAL_MODE_MASK) + +#define MOTU_CLKSRC_MASK 0x00000007 +#define MOTU_CLKSRC_INTERNAL 0 +#define MOTU_CLKSRC_ADAT_OPTICAL 1 +#define MOTU_CLKSRC_SPDIF_TOSLINK 2 +#define MOTU_CLKSRC_SMPTE 3 +#define MOTU_CLKSRC_WORDCLOCK 4 +#define MOTU_CLKSRC_ADAT_9PIN 5 +#define MOTU_CLKSRC_AES_EBU 7 +#define MOTU_CLKSRC_NONE 0xffff +#define MOTU_CLKSRC_UNCHANGED MOTU_CLKSRC_NONE + +#define MOTU_DIR_IN 1 +#define MOTU_DIR_OUT 2 +#define MOTU_DIR_INOUT (MOTU_DIR_IN | MOTU_DIR_OUT) + +/* Device registers */ +#define MOTU_REG_ISOCTRL 0x0b00 +#define MOTU_REG_OPTICAL_CTRL 0x0b10 +#define MOTU_REG_CLK_CTRL 0x0b14 +#define MOTU_REG_ROUTE_PORT_CONF 0x0c04 +#define MOTU_REG_INPUT_LEVEL 0x0c08 +#define MOTU_REG_INPUT_BOOST 0x0c14 +#define MOTU_REG_INPUT_GAIN_PAD_0 0x0c1c +#define MOTU_REG_CLKSRC_NAME0 0x0c60 + +/* Port Active Flags (ports declaration) */ +#define MOTU_PA_RATE_1x 0x0001 /* 44k1 or 48k */ +#define MOTU_PA_RATE_2x 0x0002 /* 88k2 or 96k */ +#define MOTU_PA_RATE_4x 0x0004 /* 176k4 or 192k */ +#define MOTU_PA_RATE_1x2x (MOTU_PA_RATE_1x|MOTU_PA_RATE_2x) +#define MOTU_PA_RATE_ANY (MOTU_PA_RATE_1x|MOTU_PA_RATE_2x|MOTU_PA_RATE_4x) +#define MOTU_PA_RATE_MASK MOTU_PA_RATE_ANY +#define MOTU_PA_OPTICAL_OFF 0x0010 /* Optical port off */ +#define MOTU_PA_OPTICAL_ADAT 0x0020 /* Optical port in ADAT mode */ +#define MOTU_PA_OPTICAL_TOSLINK 0x0040 /* Optical port in SPDIF/Toslink mode */ +#define MOTU_PA_OPTICAL_ON (MOTU_PA_OPTICAL_ADAT|MOTU_PA_OPTICAL_TOSLINK) +#define MOTU_PA_OPTICAL_ANY (MOTU_PA_OPTICAL_OFF|MOTU_PA_OPTICAL_ON) +#define MOTU_PA_OPTICAL_MASK MOTU_PA_OPTICAL_ANY + +class ConfigRom; +class Ieee1394Service; + +namespace Motu { + +enum EMotuModel { + MOTU_MODEL_NONE = 0x0000, + MOTU_MODEL_828mkII = 0x0001, + MOTU_MODEL_TRAVELER = 0x0002, + MOTU_MODEL_ULTRALITE= 0x0003, + MOTU_MODEL_8PRE = 0x0004, + MOTU_MODEL_828MkI = 0x0005, + MOTU_MODEL_896HD = 0x0006, +}; + +struct VendorModelEntry { + unsigned int vendor_id; + unsigned int model_id; + unsigned int unit_version; + unsigned int unit_specifier_id; + enum EMotuModel model; + const char *vendor_name; + const char *model_name; +}; + +struct PortEntry { + const char *port_name; + unsigned int port_dir; + unsigned int port_flags; + unsigned int port_offset; +}; + +struct MixerCtrl { + const char *name, *label, *desc; + unsigned int type; + unsigned int dev_register; +}; + +struct DevicePropertyEntry { + const PortEntry* port_entry; + unsigned int n_port_entries; + signed int MaxSampleRate; + const MixerCtrl *mixer_ctrl; + unsigned int n_mixer_ctrls; + // Others features can be added here like MIDI port presence. +}; + +/* Macro to calculate the size of an array */ +#define N_ELEMENTS(_array) (sizeof(_array) / sizeof((_array)[0])) + +class MotuDevice : public FFADODevice { +public: + + MotuDevice( DeviceManager& d, std::auto_ptr( configRom ) ); + virtual ~MotuDevice(); + + virtual bool buildMixer(); + virtual bool destroyMixer(); + + static bool probe( ConfigRom& configRom ); + static FFADODevice * createDevice( DeviceManager& d, std::auto_ptr( configRom )); + static int getConfigurationId( ); + virtual bool discover(); + + virtual void showDevice(); + + bool setClockCtrlRegister(signed int samplingFrequency, unsigned int clock_source); + virtual bool setSamplingFrequency( int samplingFrequency ); + virtual int getSamplingFrequency( ); + + FFADODevice::ClockSource clockIdToClockSource(unsigned int id); + virtual ClockSourceVector getSupportedClockSources(); + virtual bool setActiveClockSource(ClockSource); + virtual ClockSource getActiveClockSource(); + + virtual int getStreamCount(); + virtual Streaming::StreamProcessor *getStreamProcessorByIndex(int i); + + virtual bool prepare(); + virtual bool lock(); + virtual bool unlock(); + + virtual bool startStreamByIndex(int i); + virtual bool stopStreamByIndex(int i); + + signed int getIsoRecvChannel(void); + signed int getIsoSendChannel(void); + unsigned int getOpticalMode(unsigned int dir); + signed int setOpticalMode(unsigned int dir, unsigned int mode); + + signed int getEventSize(unsigned int dir); + + signed int m_motu_model; +protected: + struct VendorModelEntry * m_model; + signed int m_iso_recv_channel, m_iso_send_channel; + signed int m_rx_bandwidth, m_tx_bandwidth; + + Streaming::MotuReceiveStreamProcessor *m_receiveProcessor; + Streaming::MotuTransmitStreamProcessor *m_transmitProcessor; + +private: + bool addPort(Streaming::StreamProcessor *s_processor, + char *name, + enum Streaming::Port::E_Direction direction, + int position, int size); + bool addDirPorts( + enum Streaming::Port::E_Direction direction, + unsigned int sample_rate, unsigned int optical_mode); + +public: + unsigned int ReadRegister(unsigned int reg); + signed int WriteRegister(unsigned int reg, quadlet_t data); + +private: + Control::Container *m_MixerContainer; + Control::Container *m_ControlContainer; +}; + +} + +#endif Index: /branches/libffado-2.0/src/motu/motu_controls.cpp =================================================================== --- /branches/libffado-2.0/src/motu/motu_controls.cpp (revision 1158) +++ /branches/libffado-2.0/src/motu/motu_controls.cpp (revision 1158) @@ -0,0 +1,603 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * Copyright (C) 2008 by Jonathan Woithe + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +// This also includes motu_controls.h +#include "motu_avdevice.h" + +namespace Motu { + +MotuDiscreteCtrl::MotuDiscreteCtrl(MotuDevice &parent, unsigned int dev_reg) +: Control::Discrete(&parent) +, m_parent(parent) +, m_register(dev_reg) +{ +} + +MotuDiscreteCtrl::MotuDiscreteCtrl(MotuDevice &parent, unsigned int dev_reg, + std::string name, std::string label, std::string descr) +: Control::Discrete(&parent) +, m_parent(parent) +, m_register(dev_reg) +{ + setName(name); + setLabel(label); + setDescription(descr); +} + +MotuBinarySwitch::MotuBinarySwitch(MotuDevice &parent, unsigned int dev_reg, + unsigned int val_mask, unsigned int setenable_mask) +: MotuDiscreteCtrl(parent, dev_reg) +{ + m_value_mask = val_mask; + /* If no "write enable" is implemented for a given switch it's safe to + * pass zero in to setenable_mask. + */ + m_setenable_mask = setenable_mask; +} + +MotuBinarySwitch::MotuBinarySwitch(MotuDevice &parent, unsigned int dev_reg, + unsigned int val_mask, unsigned int setenable_mask, + std::string name, std::string label, std::string descr) +: MotuDiscreteCtrl(parent, dev_reg, name, label, descr) +{ + m_value_mask = val_mask; + /* If no "write enable" is implemented for a given switch it's safe to + * pass zero in to setenable_mask. + */ + m_setenable_mask = setenable_mask; +} + +bool +MotuBinarySwitch::setValue(int v) +{ + unsigned int val; + debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for switch %s (0x%04x) to %d\n", + getName().c_str(), m_register, v); + + // Set the value + if (m_setenable_mask) { + val = (v==0)?0:m_value_mask; + // Set the "write enable" bit for the value being set + val |= m_setenable_mask; + } else { + // It would be good to utilise the cached value from the receive + // processor (if running) later on. For now we'll just fetch the + // current register value directly when needed. + val = m_parent.ReadRegister(m_register); + if (v==0) + val &= ~m_value_mask; + else + val |= m_value_mask; + } + m_parent.WriteRegister(m_register, val); + + return true; +} + +int +MotuBinarySwitch::getValue() +{ + unsigned int val; + debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for switch %s (0x%04x)\n", + getName().c_str(), m_register); + + // FIXME: we could just read the appropriate mixer status field from the + // receive stream processor once we work out an efficient way to do this. + val = m_parent.ReadRegister(m_register); + return (val & m_value_mask) != 0; +} + +ChannelFader::ChannelFader(MotuDevice &parent, unsigned int dev_reg) +: MotuDiscreteCtrl(parent, dev_reg) +{ +} + +ChannelFader::ChannelFader(MotuDevice &parent, unsigned int dev_reg, + std::string name, std::string label, std::string descr) +: MotuDiscreteCtrl(parent, dev_reg, name, label, descr) +{ +} + +bool +ChannelFader::setValue(int v) +{ + unsigned int val; + debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for channel fader 0x%04x to %d\n", m_register, v); + + val = v<0?0:v; + if (val > 0x80) + val = 0x80; + // Bit 30 indicates that the channel fader is being set + val |= 0x40000000; + m_parent.WriteRegister(m_register, val); + + return true; +} + +int +ChannelFader::getValue() +{ + unsigned int val; + debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for channel fader 0x%04x\n", m_register); + + // FIXME: we could just read the appropriate mixer status field from the + // receive stream processor once we work out an efficient way to do this. + val = m_parent.ReadRegister(m_register); + return val & 0xff; +} + +ChannelPan::ChannelPan(MotuDevice &parent, unsigned int dev_reg) +: MotuDiscreteCtrl(parent, dev_reg) +{ +} + +ChannelPan::ChannelPan(MotuDevice &parent, unsigned int dev_reg, + std::string name, std::string label, std::string descr) +: MotuDiscreteCtrl(parent, dev_reg, name, label, descr) +{ +} + +bool +ChannelPan::setValue(int v) +{ + unsigned int val; + debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for channel pan 0x%04x to %d\n", m_register, v); + + val = ((v<-64?-64:v)+64) & 0xff; + if (val > 0x80) + val = 0x80; + // Bit 31 indicates that pan is being set + val = (val << 8) | 0x80000000; + m_parent.WriteRegister(m_register, val); + + return true; +} + +int +ChannelPan::getValue() +{ + unsigned int val; + debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for channel pan 0x%04x\n", m_register); + + // FIXME: we could just read the appropriate mixer status field from the + // receive stream processor once we work out an efficient way to do this. + val = m_parent.ReadRegister(m_register); + return ((val >> 8) & 0xff) - 0x40; +} + +MixFader::MixFader(MotuDevice &parent, unsigned int dev_reg) +: MotuDiscreteCtrl(parent, dev_reg) +{ +} + +MixFader::MixFader(MotuDevice &parent, unsigned int dev_reg, + std::string name, std::string label, std::string descr) +: MotuDiscreteCtrl(parent, dev_reg, name, label, descr) +{ +} + +bool +MixFader::setValue(int v) +{ + unsigned int val; + debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for mix fader 0x%04x to %d\n", m_register, v); + + val = v<0?0:v; + if (val > 0x80) + val = 0x80; + // Bit 24 indicates that the mix fader is being set + val |= 0x01000000; + m_parent.WriteRegister(m_register, val); + + return true; +} + +int +MixFader::getValue() +{ + unsigned int val; + debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for mix fader 0x%04x\n", m_register); + + // FIXME: we could just read the appropriate mixer status field from the + // receive stream processor once we work out an efficient way to do this. + val = m_parent.ReadRegister(m_register); + return val & 0xff; +} + +MixMute::MixMute(MotuDevice &parent, unsigned int dev_reg) +: MotuDiscreteCtrl(parent, dev_reg) +{ +} + +MixMute::MixMute(MotuDevice &parent, unsigned int dev_reg, + std::string name, std::string label, std::string descr) +: MotuDiscreteCtrl(parent, dev_reg, name, label, descr) +{ +} + +bool +MixMute::setValue(int v) +{ + unsigned int val, dest; + debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for mix mute 0x%04x to %d\n", m_register, v); + + // Need to read current destination so we can preserve that when setting + // mute status (mute and destination are always set together). + dest = m_parent.ReadRegister(m_register) & 0x00000f00; + // Mute status is bit 12 + val = (v==0)?0:0x00001000; + // Bit 25 indicates that mute and destination are being set. Also + // preserve the current destination. + val |= 0x02000000 | dest; + m_parent.WriteRegister(m_register, val); + + return true; +} + +int +MixMute::getValue() +{ + unsigned int val; + debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for mix mute 0x%04x\n", m_register); + + // FIXME: we could just read the appropriate mixer status field from the + // receive stream processor once we work out an efficient way to do this. + val = m_parent.ReadRegister(m_register); + return (val & 0x00001000) != 0; +} + +MixDest::MixDest(MotuDevice &parent, unsigned int dev_reg) +: MotuDiscreteCtrl(parent, dev_reg) +{ +} + +MixDest::MixDest(MotuDevice &parent, unsigned int dev_reg, + std::string name, std::string label, std::string descr) +: MotuDiscreteCtrl(parent, dev_reg, name, label, descr) +{ +} + +bool +MixDest::setValue(int v) +{ + unsigned int val, mute; + debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for mix destination 0x%04x to %d\n", m_register, v); + + // Need to get current mute status so we can preserve it + mute = m_parent.ReadRegister(m_register) & 0x00001000; + val = v; + /* Currently destination values between 0 and 0x0b are accepted. + * Ultimately this will be device (and device configuration) dependent. + */ + if (val<0 || val>0x0b) + val = 0; + /* Destination is given by bits 11-8. Add in the current mute status so + * it can be preserved (it's set concurrently with the destination). + */ + val = (val << 8) | mute; + // Bit 25 indicates that mute and destination are being set + val |= 0x02000000; + m_parent.WriteRegister(m_register, val); + + return true; +} + +int +MixDest::getValue() +{ + unsigned int val; + debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for mix destination 0x%04x\n", m_register); + + // FIXME: we could just read the appropriate mixer status field from the + // receive stream processor once we work out an efficient way to do this. + val = m_parent.ReadRegister(m_register); + return (val >> 8) & 0x0f; +} + +PhonesSrc::PhonesSrc(MotuDevice &parent) +: MotuDiscreteCtrl(parent, 0) +{ +} + +PhonesSrc::PhonesSrc(MotuDevice &parent, + std::string name, std::string label, std::string descr) +: MotuDiscreteCtrl(parent, 0, name, label, descr) +{ +} + +bool +PhonesSrc::setValue(int v) +{ + unsigned int val; + debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for phones destination to %d\n", v); + + /* Currently destination values between 0 and 0x0b are accepted. + * Ultimately this will be device (and device configuration) dependent. + */ + val = v; + if (val<0 || val>0x0b) + val = 0; + // Destination is given by bits 3-0. + // Bit 24 indicates that the phones source is being set. + val |= 0x01000000; + m_parent.WriteRegister(MOTU_REG_ROUTE_PORT_CONF, val); + + return true; +} + +int +PhonesSrc::getValue() +{ + unsigned int val; + debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for phones destination\n"); + + // FIXME: we could just read the appropriate mixer status field from the + // receive stream processor once we work out an efficient way to do this. + val = m_parent.ReadRegister(MOTU_REG_ROUTE_PORT_CONF); + return val & 0x0f; +} + +OpticalMode::OpticalMode(MotuDevice &parent, unsigned int dev_reg) +: MotuDiscreteCtrl(parent, dev_reg) +{ +} + +OpticalMode::OpticalMode(MotuDevice &parent, unsigned int dev_reg, + std::string name, std::string label, std::string descr) +: MotuDiscreteCtrl(parent, dev_reg, name, label, descr) +{ +} + +bool +OpticalMode::setValue(int v) +{ + unsigned int val; + debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for optical mode %d to %d\n", m_register, v); + + // Need to get current optical modes so we can preserve the one we're + // not setting. Input mode is in bits 9-8, output is in bits 11-10. + val = m_parent.ReadRegister(MOTU_REG_ROUTE_PORT_CONF) & 0x00000f00; + + // Set mode as requested. An invalid setting is effectively ignored. + if (v>=0 && v<=3) { + if (m_register == MOTU_DIR_IN) { + val = (val & ~0x0300) | ((v & 0x03) << 8); + } else { + val = (val & ~0x0c00) | ((v & 0x03) << 10); + } + } + // Bit 25 indicates that optical modes are being set + val |= 0x02000000; + m_parent.WriteRegister(MOTU_REG_ROUTE_PORT_CONF, val); + + return true; +} + +int +OpticalMode::getValue() +{ + unsigned int val; + debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for optical mode %d\n", m_register); + + // FIXME: we could just read the appropriate mixer status field from the + // receive stream processor once we work out an efficient way to do this. + val = m_parent.ReadRegister(MOTU_REG_ROUTE_PORT_CONF); + if (m_register == MOTU_DIR_IN) + val = (val >> 8) & 0x03; + else + val = (val >> 10) & 0x03; + return val; +} + +InputGainPad::InputGainPad(MotuDevice &parent, unsigned int channel, unsigned int mode) +: MotuDiscreteCtrl(parent, channel) +{ + m_mode = mode; + validate(); +} + +InputGainPad::InputGainPad(MotuDevice &parent, unsigned int channel, unsigned int mode, + std::string name, std::string label, std::string descr) +: MotuDiscreteCtrl(parent, channel, name, label, descr) +{ + m_mode = mode; + validate(); +} + +void InputGainPad::validate(void) { + if (m_register > MOTU_CTRL_TRIMGAINPAD_MAX_CHANNEL) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Invalid channel %d: max supported is %d, assuming 0\n", + m_register, MOTU_CTRL_TRIMGAINPAD_MAX_CHANNEL); + m_register = 0; + } + if (m_mode!=MOTU_CTRL_MODE_PAD && m_mode!=MOTU_CTRL_MODE_TRIMGAIN) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Invalid mode %d, assuming %d\n", m_mode, MOTU_CTRL_MODE_PAD); + m_mode = MOTU_CTRL_MODE_PAD; + } +} + +unsigned int InputGainPad::dev_register(void) { + /* Work out the device register to use for the associated channel */ + if (m_register>=0 && m_register<=3) { + return MOTU_REG_INPUT_GAIN_PAD_0; + } else { + debugOutput(DEBUG_LEVEL_VERBOSE, "unsupported channel %d\n", m_register); + } + return 0; +} + +bool +InputGainPad::setValue(int v) +{ + unsigned int val; + unsigned int reg, reg_shift; + debugOutput(DEBUG_LEVEL_VERBOSE, "setValue for mode %d input pad/trim %d to %d\n", m_mode, m_register, v); + + reg = dev_register(); + if (reg == 0) + return false; + reg_shift = (m_register & 0x03) * 8; + + // Need to get current gain trim / pad value so we can preserve one + // while setting the other. The pad status is in bit 6 of the channel's + // respective byte with the trim in bits 0-5. Bit 7 is the write enable + // bit for the channel. + val = m_parent.ReadRegister(reg) & (0xff << reg_shift); + + switch (m_mode) { + case MOTU_CTRL_MODE_PAD: + // Set pad bit (bit 6 of relevant channel's byte) + if (v == 0) { + val &= ~(0x40 << reg_shift); + } else { + val |= (0x40 << reg_shift); + } + break; + case MOTU_CTRL_MODE_TRIMGAIN: + // Set the gain trim (bits 0-5 of the channel's byte). Maximum + // gain is 53 dB. + if (v > 0x35) + v = 0x35; + val = (val & ~(0x3f << reg_shift)) | (v << reg_shift); + break; + default: + debugOutput(DEBUG_LEVEL_VERBOSE, "unsupported mode %d\n", m_mode); + return false; + } + + // Set the channel's write enable bit + val |= (0x80 << reg_shift); + + m_parent.WriteRegister(reg, val); + + return true; +} + +int +InputGainPad::getValue() +{ + unsigned int val; + unsigned int reg, reg_shift; + debugOutput(DEBUG_LEVEL_VERBOSE, "getValue for mode %d input pad/trim %d\n", m_mode, m_register); + + reg = dev_register(); + if (reg == 0) + return false; + reg_shift = (m_register & 0x03) * 8; + + // The pad status is in bit 6 of the channel's respective byte with the + // trim in bits 0-5. Bit 7 is the write enable bit for the channel. + val = m_parent.ReadRegister(reg); + + switch (m_mode) { + case MOTU_CTRL_MODE_PAD: + val = ((val >> reg_shift) & 0x40) != 0; + break; + case MOTU_CTRL_MODE_TRIMGAIN: + val = ((val >> reg_shift) & 0x3f); + break; + default: + debugOutput(DEBUG_LEVEL_VERBOSE, "unsupported mode %d\n", m_mode); + return 0; + } + + return val; +} + +InfoElement::InfoElement(MotuDevice &parent, unsigned infotype) +: MotuDiscreteCtrl(parent, infotype) +{ +} + +InfoElement::InfoElement(MotuDevice &parent, unsigned infotype, + std::string name, std::string label, std::string descr) +: MotuDiscreteCtrl(parent, infotype, name, label, descr) +{ +} + +bool +InfoElement::setValue(int v) +{ + /* This is a read-only field, so any call to setValue() is technically + * an error. + */ + debugOutput(DEBUG_LEVEL_VERBOSE, "InfoElement (%d) is read-only\n", m_register); + return false; +} + +int +InfoElement::getValue() +{ + unsigned int val; + signed int res = 0; + + switch (m_register) { + case MOTU_INFO_IS_STREAMING: + val = m_parent.ReadRegister(MOTU_REG_ISOCTRL); + /* Streaming is active if either bit 22 (Motu->PC streaming + * enable) or bit 30 (PC->Motu streaming enable) is set. + */ + res = (val & 0x40400000) != 0; + debugOutput(DEBUG_LEVEL_VERBOSE, "IsStreaming: %d (reg=%08x)\n", res, val); + break; + case MOTU_INFO_SAMPLE_RATE: + res = m_parent.getSamplingFrequency(); + debugOutput(DEBUG_LEVEL_VERBOSE, "SampleRate: %d\n", res); + break; + case MOTU_INFO_HAS_MIC_INPUTS: + /* Only the 828Mk2 has separate mic inputs. In time this may be + * deduced by walking the port info array within the parent. + */ + res = m_parent.m_motu_model == MOTU_MODEL_828mkII ? 1:0; + debugOutput(DEBUG_LEVEL_VERBOSE, "Has mic inputs: %d\n", res); + break; + case MOTU_INFO_HAS_AESEBU_INPUTS: + /* AES/EBU inputs are currently present on the Traveler and + * 896HD. In time this may be deduced by walking the port info + * array within the parent. + */ + val = m_parent.m_motu_model; + res = (val==MOTU_MODEL_TRAVELER || val==MOTU_MODEL_896HD); + debugOutput(DEBUG_LEVEL_VERBOSE, "HasAESEBUInputs: %d\n", res); + break; + case MOTU_INFO_HAS_SPDIF_INPUTS: + /* SPDIF inputs are present on all supported models except the + * 896HD and the 8pre. In time this may be deduced by walking + * the port info array within the parent. + */ + val = m_parent.m_motu_model; + res = (val!=MOTU_MODEL_8PRE && val!=MOTU_MODEL_896HD); + debugOutput(DEBUG_LEVEL_VERBOSE, "HasSPDIFInputs: %d\n", res); + break; + case MOTU_INFO_HAS_OPTICAL_SPDIF: + /* THe 896HD doesn't have optical SPDIF capability */ + val = m_parent.m_motu_model; + res = (val != MOTU_MODEL_896HD); + debugOutput(DEBUG_LEVEL_VERBOSE, "HasOpticalSPDIF: %d\n", res); + break; + } + return res; +} + +} Index: /branches/libffado-2.0/src/motu/motu_avdevice.cpp =================================================================== --- /branches/libffado-2.0/src/motu/motu_avdevice.cpp (revision 1158) +++ /branches/libffado-2.0/src/motu/motu_avdevice.cpp (revision 1158) @@ -0,0 +1,1424 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * Copyright (C) 2005-2008 by Jonathan Woithe + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "motu/motu_avdevice.h" + +#include "libieee1394/configrom.h" +#include "libieee1394/ieee1394service.h" + +#include "libavc/avc_definitions.h" + +#include "debugmodule/debugmodule.h" + +#include "libstreaming/motu/MotuReceiveStreamProcessor.h" +#include "libstreaming/motu/MotuTransmitStreamProcessor.h" +#include "libstreaming/motu/MotuPort.h" + +#include "libutil/DelayLockedLoop.h" +#include "libutil/Time.h" + +#include "libcontrol/BasicElements.h" + +#include +#include +#include +#include "libutil/ByteSwap.h" +#include +#include + +#include + +namespace Motu { + +// Define the supported devices. Device ordering is arbitary here. +static VendorModelEntry supportedDeviceList[] = +{ +// {vendor_id, model_id, unit_version, unit_specifier_id, model, vendor_name,model_name} + {FW_VENDORID_MOTU, 0, 0x00000003, 0x000001f2, MOTU_MODEL_828mkII, "MOTU", "828MkII"}, + {FW_VENDORID_MOTU, 0, 0x00000009, 0x000001f2, MOTU_MODEL_TRAVELER, "MOTU", "Traveler"}, + {FW_VENDORID_MOTU, 0, 0x0000000d, 0x000001f2, MOTU_MODEL_ULTRALITE, "MOTU", "UltraLite"}, + {FW_VENDORID_MOTU, 0, 0x0000000f, 0x000001f2, MOTU_MODEL_8PRE, "MOTU", "8pre"}, + {FW_VENDORID_MOTU, 0, 0x00000001, 0x000001f2, MOTU_MODEL_828MkI, "MOTU", "828MkI"}, + {FW_VENDORID_MOTU, 0, 0x00000005, 0x000001f2, MOTU_MODEL_896HD, "MOTU", "896HD"}, +}; + +// Ports declarations +const PortEntry Ports_828MKI[] = +{ + {"Analog1", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 10}, + {"Analog2", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 13}, + {"Analog3", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 16}, + {"Analog4", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 19}, + {"Analog5", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 22}, + {"Analog6", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 25}, + {"Analog7", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 28}, + {"Analog8", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 31}, + {"SPDIF1", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 34}, + {"SPDIF2", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 37}, + {"ADAT1", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 40}, + {"ADAT2", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 43}, + {"ADAT3", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 46}, + {"ADAT4", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 49}, + {"ADAT5", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 52}, + {"ADAT6", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 55}, + {"ADAT7", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 58}, + {"ADAT8", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 61}, +}; + +const PortEntry Ports_896HD[] = +{ + {"Mix-L", MOTU_DIR_IN, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 10}, + {"Mix-R", MOTU_DIR_IN, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 13}, + {"Phones-L", MOTU_DIR_OUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 10}, + {"Phones-R", MOTU_DIR_OUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 13}, + {"Analog1", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 16}, + {"Analog1", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 10}, + {"Analog2", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 19}, + {"Analog2", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 13}, + {"Analog3", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 22}, + {"Analog3", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 16}, + {"Analog4", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 25}, + {"Analog4", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 19}, + {"Analog5", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 28}, + {"Analog5", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 22}, + {"Analog6", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 31}, + {"Analog6", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 25}, + {"Analog7", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 34}, + {"Analog7", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 28}, + {"Analog8", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 37}, + {"Analog8", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 31}, + {"MainOut-L", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 40}, + {"MainOut-R", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 43}, + {"AES/EBU1", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 46}, + {"AES/EBU2", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 49}, + {"ADAT1", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ADAT, 52}, + {"ADAT2", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ADAT, 55}, + {"ADAT3", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ADAT, 58}, + {"ADAT4", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ADAT, 61}, + {"ADAT5", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 64}, + {"ADAT6", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 67}, + {"ADAT7", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 70}, + {"ADAT8", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 73}, +}; + +const PortEntry Ports_828MKII[] = +{ + {"Main-L", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 40}, + {"Main-R", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 43}, + {"Mix-L", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 10}, + {"Mix-R", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 13}, + {"Analog1", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 16}, + {"Analog2", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 19}, + {"Analog3", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 22}, + {"Analog4", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 25}, + {"Analog5", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 28}, + {"Analog6", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 31}, + {"Analog7", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 34}, + {"Analog8", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 37}, + {"Phones-L", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 10}, + {"Phones-R", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 13}, + {"Mic1", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 40}, + {"Mic2", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 43}, + {"SPDIF1", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 46}, + {"SPDIF2", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 49}, + {"ADAT1", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 52}, + {"ADAT2", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 55}, + {"ADAT3", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 58}, + {"ADAT4", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 61}, + {"ADAT5", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 64}, + {"ADAT6", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 67}, + {"ADAT7", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 70}, + {"ADAT8", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 73}, +}; + +const PortEntry Ports_TRAVELER[] = +{ + {"Mix-L", MOTU_DIR_IN, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 10}, + {"Mix-R", MOTU_DIR_IN, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 13}, + {"Phones-L", MOTU_DIR_OUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 10}, + {"Phones-R", MOTU_DIR_OUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 13}, + {"Analog1", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 16}, + {"Analog1", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 10}, + {"Analog2", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 19}, + {"Analog2", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 13}, + {"Analog3", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 22}, + {"Analog3", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 16}, + {"Analog4", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 25}, + {"Analog4", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 19}, + {"Analog5", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 28}, + {"Analog5", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 22}, + {"Analog6", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 31}, + {"Analog6", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 25}, + {"Analog7", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 34}, + {"Analog7", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 28}, + {"Analog8", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 37}, + {"Analog8", MOTU_DIR_INOUT, MOTU_PA_RATE_4x|MOTU_PA_OPTICAL_ANY, 31}, + {"AES/EBU1", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 40}, + {"AES/EBU2", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ANY, 43}, + {"SPDIF1", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_OFF|MOTU_PA_OPTICAL_ADAT, 46}, + {"SPDIF2", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_OFF|MOTU_PA_OPTICAL_ADAT, 49}, + {"Toslink1", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_TOSLINK, 46}, + {"Toslink2", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_TOSLINK, 49}, + {"ADAT1", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ADAT, 52}, + {"ADAT2", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ADAT, 55}, + {"ADAT3", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ADAT, 58}, + {"ADAT4", MOTU_DIR_INOUT, MOTU_PA_RATE_1x2x|MOTU_PA_OPTICAL_ADAT, 61}, + {"ADAT5", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 64}, + {"ADAT6", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 67}, + {"ADAT7", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 70}, + {"ADAT8", MOTU_DIR_INOUT, MOTU_PA_RATE_1x|MOTU_PA_OPTICAL_ADAT, 73}, +}; + +const PortEntry Ports_ULTRALITE[] = +{ + {"Main-L", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 40}, + {"Main-R", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 43}, + {"Mix-L", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 10}, + {"Mix-R", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 13}, + {"Mic1", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 16}, + {"Mic2", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 19}, + {"Analog1", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 16}, + {"Analog2", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 19}, + {"Analog3", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 22}, + {"Analog4", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 25}, + {"Analog5", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 28}, + {"Analog6", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 31}, + {"Analog7", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 34}, + {"Analog8", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 37}, + {"Phones-L", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 10}, + {"Phones-R", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 13}, + {"SPDIF1", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 46}, + {"SPDIF2", MOTU_DIR_INOUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 49}, +}; + +const PortEntry Ports_8PRE[] = +{ + {"Analog1", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 16}, + {"Analog2", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 19}, + {"Analog3", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 22}, + {"Analog4", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 25}, + {"Analog5", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 28}, + {"Analog6", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 31}, + {"Analog7", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 34}, + {"Analog8", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 37}, + {"Mix-L", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 10}, + {"Mix-R", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 13}, + {"Main-L", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 16}, + {"Main-R", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 19}, + {"Phones-L", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 10}, + {"Phones-R", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ANY, 13}, + {"ADAT1", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 40}, + {"ADAT1", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 22}, + {"ADAT2", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 43}, + {"ADAT2", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 25}, + {"ADAT3", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 46}, + {"ADAT3", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 28}, + {"ADAT4", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 49}, + {"ADAT4", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 31}, + {"ADAT5", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 52}, + {"ADAT5", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 34}, + {"ADAT6", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 55}, + {"ADAT6", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 37}, + {"ADAT7", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 58}, + {"ADAT7", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 40}, + {"ADAT8", MOTU_DIR_IN, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 61}, + {"ADAT8", MOTU_DIR_OUT, MOTU_PA_RATE_ANY|MOTU_PA_OPTICAL_ADAT, 43}, +}; + +// Mixer registers +const MixerCtrl MixerCtrls_Traveler[] = { + {"Mix1/Ana1_", "Mix 1 analog 1 ", "", MOTU_CTRL_STD_CHANNEL, 0x4000, }, + {"Mix1/Ana2_", "Mix 1 analog 2 ", "", MOTU_CTRL_STD_CHANNEL, 0x4004, }, + {"Mix1/Ana3_", "Mix 1 analog 3 ", "", MOTU_CTRL_STD_CHANNEL, 0x4008, }, + {"Mix1/Ana4_", "Mix 1 analog 4 ", "", MOTU_CTRL_STD_CHANNEL, 0x400c, }, + {"Mix1/Ana5_", "Mix 1 analog 5 ", "", MOTU_CTRL_STD_CHANNEL, 0x4010, }, + {"Mix1/Ana6_", "Mix 1 analog 6 ", "", MOTU_CTRL_STD_CHANNEL, 0x4014, }, + {"Mix1/Ana7_", "Mix 1 analog 7 ", "", MOTU_CTRL_STD_CHANNEL, 0x4018, }, + {"Mix1/Ana8_", "Mix 1 analog 8 ", "", MOTU_CTRL_STD_CHANNEL, 0x401c, }, + {"Mix1/Adat1_", "Mix 1 adat 1 ", "", MOTU_CTRL_STD_CHANNEL, 0x4030, }, + {"Mix1/Adat2_", "Mix 1 adat 2 ", "", MOTU_CTRL_STD_CHANNEL, 0x4034, }, + {"Mix1/Adat3_", "Mix 1 adat 3 ", "", MOTU_CTRL_STD_CHANNEL, 0x4038, }, + {"Mix1/Adat4_", "Mix 1 adat 4 ", "", MOTU_CTRL_STD_CHANNEL, 0x403c, }, + {"Mix1/Adat5_", "Mix 1 adat 5 ", "", MOTU_CTRL_STD_CHANNEL, 0x4040, }, + {"Mix1/Adat6_", "Mix 1 adat 6 ", "", MOTU_CTRL_STD_CHANNEL, 0x4044, }, + {"Mix1/Adat7_", "Mix 1 adat 7 ", "", MOTU_CTRL_STD_CHANNEL, 0x4048, }, + {"Mix1/Adat8_", "Mix 1 adat 8 ", "", MOTU_CTRL_STD_CHANNEL, 0x404c, }, + {"Mix1/Aes1_", "Mix 1 AES/EBU 1 ", "", MOTU_CTRL_STD_CHANNEL, 0x4020, }, + {"Mix1/Aes2_", "Mix 1 AES/EBU 2 ", "", MOTU_CTRL_STD_CHANNEL, 0x4024, }, + {"Mix1/Spdif1_", "Mix 1 SPDIF 1 ", "", MOTU_CTRL_STD_CHANNEL, 0x4028, }, + {"Mix1/Spdif2_", "Mix 1 SPDIF 2 ", "", MOTU_CTRL_STD_CHANNEL, 0x402c, }, + + {"Mix1/Mix_", "Mix 1 ", "", MOTU_CTRL_STD_MIX, 0x0c20, }, + + /* For mic/line input controls, the "register" is the zero-based channel number */ + {"Control/Ana1_", "Analog 1 input ", "", MOTU_CTRL_TRAVELER_MIC_INPUT_CTRLS, 0}, + {"Control/Ana2_", "Analog 2 input ", "", MOTU_CTRL_TRAVELER_MIC_INPUT_CTRLS, 1}, + {"Control/Ana3_", "Analog 3 input ", "", MOTU_CTRL_TRAVELER_MIC_INPUT_CTRLS, 2}, + {"Control/Ana4_", "Analog 4 input ", "", MOTU_CTRL_TRAVELER_MIC_INPUT_CTRLS, 3}, + {"Control/Ana5_", "Analog 5 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 4}, + {"Control/Ana6_", "Analog 6 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 5}, + {"Control/Ana7_", "Analog 7 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 6}, + {"Control/Ana8_", "Analog 8 input ", "", MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS, 7}, + + {"Control/Phones_", "Phones source", "", MOTU_CTRL_PHONES_SRC, 0}, + + {"Control/OpticalIn_mode", "Optical input mode ", "", MOTU_CTRL_OPTICAL_MODE, MOTU_DIR_IN}, + {"Control/OpticalOut_mode", "Optical output mode ", "", MOTU_CTRL_OPTICAL_MODE, MOTU_DIR_OUT}, +}; + +// For convenience during initial testing, just make the 828MkII and 896HD +// use the Traveler's mixer definition. Separate definitions for these +// models will come once the final mixer structure is in place. For now +// it's in a state of flux and subject to significant change. +#define MixerCtrls_828MkII MixerCtrls_Traveler +#define MixerCtrls_896HD MixerCtrls_Traveler + +/* The order of DevicesProperty entries must match the numeric order of the + * MOTU model enumeration (EMotuModel). + */ +const DevicePropertyEntry DevicesProperty[] = { +// { Ports_map, N_ELEMENTS( Ports_map ), MaxSR }, + { Ports_828MKII, N_ELEMENTS( Ports_828MKII ), 96000, MixerCtrls_828MkII, N_ELEMENTS(MixerCtrls_828MkII), }, + { Ports_TRAVELER, N_ELEMENTS( Ports_TRAVELER ), 192000, MixerCtrls_Traveler, N_ELEMENTS(MixerCtrls_Traveler), }, + { Ports_ULTRALITE, N_ELEMENTS( Ports_ULTRALITE ), 96000 }, + { Ports_8PRE, N_ELEMENTS( Ports_8PRE ), 96000 }, + { Ports_828MKI, N_ELEMENTS( Ports_828MKI ), 48000 }, + { Ports_896HD, N_ELEMENTS( Ports_896HD ), 192000, MixerCtrls_896HD, N_ELEMENTS(MixerCtrls_896HD), }, +}; + +MotuDevice::MotuDevice( DeviceManager& d, std::auto_ptr( configRom )) + : FFADODevice( d, configRom ) + , m_motu_model( MOTU_MODEL_NONE ) + , m_iso_recv_channel ( -1 ) + , m_iso_send_channel ( -1 ) + , m_rx_bandwidth ( -1 ) + , m_tx_bandwidth ( -1 ) + , m_receiveProcessor ( 0 ) + , m_transmitProcessor ( 0 ) + , m_MixerContainer ( NULL ) + , m_ControlContainer ( NULL ) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Created Motu::MotuDevice (NodeID %d)\n", + getConfigRom().getNodeId() ); +} + +MotuDevice::~MotuDevice() +{ + delete m_receiveProcessor; + delete m_transmitProcessor; + + // Free ieee1394 bus resources if they have been allocated + if (m_iso_recv_channel>=0 && !get1394Service().freeIsoChannel(m_iso_recv_channel)) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Could not free recv iso channel %d\n", m_iso_recv_channel); + } + if (m_iso_send_channel>=0 && !get1394Service().freeIsoChannel(m_iso_send_channel)) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Could not free send iso channel %d\n", m_iso_send_channel); + } + + destroyMixer(); +} + +bool +MotuDevice::buildMixer() { + unsigned int i; + bool result = true; + debugOutput(DEBUG_LEVEL_VERBOSE, "Building a MOTU mixer...\n"); + + destroyMixer(); + + // create the mixer object container + m_MixerContainer = new Control::Container(this, "Mixer"); + if (!m_MixerContainer) { + debugError("Could not create mixer container...\n"); + return false; + } + + // Mixer controls get added here + for (i=0; iaddElement( + new ChannelFader(*this, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].dev_register, + name, label, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].desc)); + type &= ~MOTU_CTRL_CHANNEL_FADER; + } + if (type & MOTU_CTRL_CHANNEL_PAN) { + snprintf(name, 100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].name, "pan"); + snprintf(label,100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].label,"pan"); + result &= m_MixerContainer->addElement( + new ChannelPan(*this, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].dev_register, + name, label, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].desc)); + type &= ~MOTU_CTRL_CHANNEL_PAN; + } + if (type & MOTU_CTRL_CHANNEL_MUTE) { + snprintf(name, 100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].name, "mute"); + snprintf(label,100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].label,"mute"); + result &= m_MixerContainer->addElement( + new MotuBinarySwitch(*this, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].dev_register, + MOTU_CTRL_MASK_MUTE_VALUE, MOTU_CTRL_MASK_MUTE_SETENABLE, + name, label, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].desc)); + type &= ~MOTU_CTRL_CHANNEL_MUTE; + } + if (type & MOTU_CTRL_CHANNEL_SOLO) { + snprintf(name, 100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].name, "solo"); + snprintf(label,100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].label,"solo"); + result &= m_MixerContainer->addElement( + new MotuBinarySwitch(*this, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].dev_register, + MOTU_CTRL_MASK_SOLO_VALUE, MOTU_CTRL_MASK_SOLO_SETENABLE, + name, label, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].desc)); + type &= ~MOTU_CTRL_CHANNEL_SOLO; + } + + if (type & MOTU_CTRL_MIX_FADER) { + snprintf(name, 100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].name, "fader"); + snprintf(label,100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].label,"fader"); + result &= m_MixerContainer->addElement( + new MixFader(*this, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].dev_register, + name, label, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].desc)); + type &= ~MOTU_CTRL_MIX_FADER; + } + if (type & MOTU_CTRL_MIX_MUTE) { + snprintf(name, 100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].name, "mute"); + snprintf(label,100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].label,"mute"); + result &= m_MixerContainer->addElement( + new MixMute(*this, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].dev_register, + name, label, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].desc)); + type &= ~MOTU_CTRL_MIX_MUTE; + } + if (type & MOTU_CTRL_MIX_DEST) { + snprintf(name, 100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].name, "dest"); + snprintf(label,100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].label,"dest"); + result &= m_MixerContainer->addElement( + new MixDest(*this, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].dev_register, + name, label, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].desc)); + type &= ~MOTU_CTRL_MIX_DEST; + } + + if (type & MOTU_CTRL_INPUT_TRIMGAIN) { + snprintf(name, 100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].name, "trimgain"); + snprintf(label,100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].label,"trimgain"); + result &= m_MixerContainer->addElement( + new InputGainPad(*this, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].dev_register, MOTU_CTRL_MODE_TRIMGAIN, + name, label, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].desc)); + type &= ~MOTU_CTRL_INPUT_TRIMGAIN; + } + if (type & MOTU_CTRL_INPUT_PAD) { + snprintf(name, 100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].name, "pad"); + snprintf(label,100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].label,"pad"); + result &= m_MixerContainer->addElement( + new InputGainPad(*this, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].dev_register, MOTU_CTRL_MODE_PAD, + name, label, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].desc)); + type &= ~MOTU_CTRL_INPUT_PAD; + } + + if (type & MOTU_CTRL_INPUT_LEVEL) { + snprintf(name, 100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].name, "level"); + snprintf(label,100, "%s%s", DevicesProperty[m_motu_model-1].mixer_ctrl[i].label,"level"); + result &= m_MixerContainer->addElement( + new MotuBinarySwitch(*this, + MOTU_REG_INPUT_LEVEL, + 1<addElement( + new MotuBinarySwitch(*this, + MOTU_REG_INPUT_BOOST, + 1<addElement( + new PhonesSrc(*this, + name, label, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].desc)); + type &= ~MOTU_CTRL_PHONES_SRC; + } + if (type & MOTU_CTRL_OPTICAL_MODE) { + result &= m_MixerContainer->addElement( + new OpticalMode(*this, DevicesProperty[m_motu_model-1].mixer_ctrl[i].dev_register, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].name, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].label, + DevicesProperty[m_motu_model-1].mixer_ctrl[i].desc)); + type &= ~MOTU_CTRL_OPTICAL_MODE; + } + + if (type) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Unknown mixer control type flag bits 0x%08x\n", DevicesProperty[m_motu_model-1].mixer_ctrl[i].type); + } + } + + /* Now add some general device information controls. These may yet + * become device-specific if it turns out to be easier that way. + */ + result &= m_MixerContainer->addElement( + new InfoElement(*this, MOTU_INFO_IS_STREAMING, "Info/IsStreaming", "Is device streaming", "")); + result &= m_MixerContainer->addElement( + new InfoElement(*this, MOTU_INFO_SAMPLE_RATE, "Info/SampleRate", "Device sample rate", "")); + result &= m_MixerContainer->addElement( + new InfoElement(*this, MOTU_INFO_HAS_MIC_INPUTS, "Info/HasMicInputs", "Device has mic inputs", "")); + result &= m_MixerContainer->addElement( + new InfoElement(*this, MOTU_INFO_HAS_AESEBU_INPUTS, "Info/HasAESEBUInputs", "Device has AES/EBU inputs", "")); + result &= m_MixerContainer->addElement( + new InfoElement(*this, MOTU_INFO_HAS_SPDIF_INPUTS, "Info/HasSPDIFInputs", "Device has SPDIF inputs", "")); + result &= m_MixerContainer->addElement( + new InfoElement(*this, MOTU_INFO_HAS_OPTICAL_SPDIF, "Info/HasOpticalSPDIF", "Device has Optical SPDIF", "")); + + if (!addElement(m_MixerContainer)) { + debugWarning("Could not register mixer to device\n"); + // clean up + destroyMixer(); + return false; + } + + // Special controls + m_ControlContainer = new Control::Container(this, "Control"); + if (!m_ControlContainer) { + debugError("Could not create control container...\n"); + return false; + } + + // Special controls get added here + + if (!result) { + debugWarning("One or more device control elements could not be created."); + // clean up those that couldn't be created + destroyMixer(); + return false; + } + if (!addElement(m_ControlContainer)) { + debugWarning("Could not register controls to device\n"); + // clean up + destroyMixer(); + return false; + } + + return true; +} + + +bool +MotuDevice::destroyMixer() { + debugOutput(DEBUG_LEVEL_VERBOSE, "destroy mixer...\n"); + + if (m_MixerContainer == NULL) { + debugOutput(DEBUG_LEVEL_VERBOSE, "no mixer to destroy...\n"); + return true; + } + + if (!deleteElement(m_MixerContainer)) { + debugError("Mixer present but not registered to the avdevice\n"); + return false; + } + + // remove and delete (as in free) child control elements + m_MixerContainer->clearElements(true); + delete m_MixerContainer; + m_MixerContainer = NULL; + + // remove control container + if (m_ControlContainer == NULL) { + debugOutput(DEBUG_LEVEL_VERBOSE, "no controls to destroy...\n"); + return true; + } + + if (!deleteElement(m_ControlContainer)) { + debugError("Controls present but not registered to the avdevice\n"); + return false; + } + + // remove and delete (as in free) child control elements + m_ControlContainer->clearElements(true); + delete m_ControlContainer; + m_ControlContainer = NULL; + + return true; +} + +bool +MotuDevice::probe( ConfigRom& configRom ) +{ + unsigned int vendorId = configRom.getNodeVendorId(); + unsigned int unitVersion = configRom.getUnitVersion(); + unsigned int unitSpecifierId = configRom.getUnitSpecifierId(); + + for ( unsigned int i = 0; + i < ( sizeof( supportedDeviceList )/sizeof( VendorModelEntry ) ); + ++i ) + { + if ( ( supportedDeviceList[i].vendor_id == vendorId ) + && ( supportedDeviceList[i].unit_version == unitVersion ) + && ( supportedDeviceList[i].unit_specifier_id == unitSpecifierId ) + ) + { + return true; + } + } + + return false; +} + +FFADODevice * +MotuDevice::createDevice(DeviceManager& d, std::auto_ptr( configRom )) +{ + return new MotuDevice(d, configRom); +} + +bool +MotuDevice::discover() +{ + unsigned int vendorId = getConfigRom().getNodeVendorId(); + unsigned int unitVersion = getConfigRom().getUnitVersion(); + unsigned int unitSpecifierId = getConfigRom().getUnitSpecifierId(); + + for ( unsigned int i = 0; + i < ( sizeof( supportedDeviceList )/sizeof( VendorModelEntry ) ); + ++i ) + { + if ( ( supportedDeviceList[i].vendor_id == vendorId ) + && ( supportedDeviceList[i].unit_version == unitVersion ) + && ( supportedDeviceList[i].unit_specifier_id == unitSpecifierId ) + ) + { + m_model = &(supportedDeviceList[i]); + m_motu_model=supportedDeviceList[i].model; + } + } + + if (m_model == NULL) { + return false; + } + + debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n", + m_model->vendor_name, m_model->model_name); + + if (!buildMixer()) { + debugWarning("Could not build mixer\n"); + } + + return true; +} + +int +MotuDevice::getSamplingFrequency( ) { +/* + * Retrieve the current sample rate from the MOTU device. + */ + quadlet_t q = ReadRegister(MOTU_REG_CLK_CTRL); + int rate = 0; + + switch (q & MOTU_RATE_BASE_MASK) { + case MOTU_RATE_BASE_44100: + rate = 44100; + break; + case MOTU_RATE_BASE_48000: + rate = 48000; + break; + } + switch (q & MOTU_RATE_MULTIPLIER_MASK) { + case MOTU_RATE_MULTIPLIER_2X: + rate *= 2; + break; + case MOTU_RATE_MULTIPLIER_4X: + rate *= 4; + break; + } + return rate; +} + +int +MotuDevice::getConfigurationId() +{ + return 0; +} + +bool +MotuDevice::setClockCtrlRegister(signed int samplingFrequency, unsigned int clock_source) +{ +/* + * Set the MOTU device's samplerate and/or clock source via the clock + * control register. If samplingFrequency <= 0 it remains unchanged. If + * clock_source is MOTU_CLKSRC_UNCHANGED the clock source remains unchanged. + */ + char *src_name; + quadlet_t q, new_rate=0xffffffff; + int i, supported=true, cancel_adat=false; + quadlet_t reg; + + /* Don't touch anything if there's nothing to do */ + if (samplingFrequency<=0 && clock_source==MOTU_CLKSRC_NONE) + return true; + + if ( samplingFrequency > DevicesProperty[m_motu_model-1].MaxSampleRate ) + return false; + + reg = ReadRegister(MOTU_REG_CLK_CTRL); + + switch ( samplingFrequency ) { + case -1: + break; + case 44100: + new_rate = MOTU_RATE_BASE_44100 | MOTU_RATE_MULTIPLIER_1X; + break; + case 48000: + new_rate = MOTU_RATE_BASE_48000 | MOTU_RATE_MULTIPLIER_1X; + break; + case 88200: + new_rate = MOTU_RATE_BASE_44100 | MOTU_RATE_MULTIPLIER_2X; + break; + case 96000: + new_rate = MOTU_RATE_BASE_48000 | MOTU_RATE_MULTIPLIER_2X; + break; + case 176400: + new_rate = MOTU_RATE_BASE_44100 | MOTU_RATE_MULTIPLIER_4X; + cancel_adat = true; // current ADAT protocol doesn't support sample rate > 96000 + break; + case 192000: + new_rate = MOTU_RATE_BASE_48000 | MOTU_RATE_MULTIPLIER_4X; + cancel_adat = true; + break; + default: + supported=false; + } + + // Sanity check the clock source + if ((clock_source>7 || clock_source==6) && clock_source!=MOTU_CLKSRC_UNCHANGED) + supported = false; + + // Update the clock control register. FIXME: while this is now rather + // comprehensive there may still be a need to manipulate MOTU_REG_CLK_CTRL + // a little more than we do. + if (supported) { + + // If optical port must be disabled (because a 4x sample rate has + // been selected) then do so before changing the sample rate. At + // this stage it will be up to the user to re-enable the optical + // port if the sample rate is set to a 1x or 2x rate later. + if (cancel_adat) { + setOpticalMode(MOTU_DIR_INOUT, MOTU_OPTICAL_MODE_OFF); + } + + // Set up new frequency if requested + if (new_rate != 0xffffffff) { + reg &= ~(MOTU_RATE_BASE_MASK|MOTU_RATE_MULTIPLIER_MASK); + reg |= new_rate; + } + + // Set up new clock source if required + if (clock_source != MOTU_CLKSRC_UNCHANGED) { + reg &= ~MOTU_CLKSRC_MASK; + reg |= (clock_source & MOTU_CLKSRC_MASK); + } + + // In other OSes bit 26 of MOTU_REG_CLK_CTRL always seems + // to be set when this register is written to although the + // reason isn't currently known. When we set it, it appears + // to prevent output being produced so we'll leave it unset + // until we work out what's going on. Other systems write + // to MOTU_REG_CLK_CTRL multiple times, so that may be + // part of the mystery. + // value |= 0x04000000; + if (WriteRegister(MOTU_REG_CLK_CTRL, reg) == 0) { + supported=true; + } else { + supported=false; + } + // A write to the rate/clock control register requires the + // textual name of the current clock source be sent to the + // clock source name registers. + switch (reg & MOTU_CLKSRC_MASK) { + case MOTU_CLKSRC_INTERNAL: + src_name = "Internal "; + break; + case MOTU_CLKSRC_ADAT_OPTICAL: + src_name = "ADAT Optical "; + break; + case MOTU_CLKSRC_SPDIF_TOSLINK: + if (getOpticalMode(MOTU_DIR_IN) == MOTU_OPTICAL_MODE_TOSLINK) + src_name = "TOSLink "; + else + src_name = "SPDIF "; + break; + case MOTU_CLKSRC_SMPTE: + src_name = "SMPTE "; + break; + case MOTU_CLKSRC_WORDCLOCK: + src_name = "Word Clock In "; + break; + case MOTU_CLKSRC_ADAT_9PIN: + src_name = "ADAT 9-pin "; + break; + case MOTU_CLKSRC_AES_EBU: + src_name = "AES-EBU "; + break; + default: + src_name = "Unknown "; + } + for (i=0; i<16; i+=4) { + q = (src_name[i]<<24) | (src_name[i+1]<<16) | + (src_name[i+2]<<8) | src_name[i+3]; + WriteRegister(MOTU_REG_CLKSRC_NAME0+i, q); + } + } + return supported; +} + +bool +MotuDevice::setSamplingFrequency( int samplingFrequency ) +{ +/* + * Set the MOTU device's samplerate. + */ + return setClockCtrlRegister(samplingFrequency, MOTU_CLKSRC_UNCHANGED); +} + +FFADODevice::ClockSource +MotuDevice::clockIdToClockSource(unsigned int id) { + ClockSource s; + s.id = id; + + // Assume a clock source is valid/active unless otherwise overridden. + s.valid = true; + s.locked = true; + s.active = true; + + switch (id) { + case MOTU_CLKSRC_INTERNAL: + s.type = eCT_Internal; + s.description = "Internal sync"; + break; + case MOTU_CLKSRC_ADAT_OPTICAL: + s.type = eCT_ADAT; + s.description = "ADAT optical"; + break; + case MOTU_CLKSRC_SPDIF_TOSLINK: + s.type = eCT_SPDIF; + s.description = "SPDIF/Toslink"; + break; + case MOTU_CLKSRC_SMPTE: + s.type = eCT_SMPTE; + s.description = "SMPTE"; + // Since we don't currently know how to deal with SMPTE on these devices + // make sure the SMPTE clock source is disabled. + s.valid = false; + s.active = false; + s.locked = false; + break; + case MOTU_CLKSRC_WORDCLOCK: + s.type = eCT_WordClock; + s.description = "Wordclock"; + break; + case MOTU_CLKSRC_ADAT_9PIN: + s.type = eCT_ADAT; + s.description = "ADAT 9-pin"; + break; + case MOTU_CLKSRC_AES_EBU: + s.type = eCT_AES; + s.description = "AES/EBU"; + break; + default: + s.type = eCT_Invalid; + } + + s.slipping = false; + return s; +} + +FFADODevice::ClockSourceVector +MotuDevice::getSupportedClockSources() { + FFADODevice::ClockSourceVector r; + ClockSource s; + + /* Form a list of clocks supported by MOTU interfaces */ + s = clockIdToClockSource(MOTU_CLKSRC_INTERNAL); + r.push_back(s); + s = clockIdToClockSource(MOTU_CLKSRC_ADAT_OPTICAL); + r.push_back(s); + s = clockIdToClockSource(MOTU_CLKSRC_SPDIF_TOSLINK); + r.push_back(s); + s = clockIdToClockSource(MOTU_CLKSRC_SMPTE); + r.push_back(s); + s = clockIdToClockSource(MOTU_CLKSRC_WORDCLOCK); + r.push_back(s); + s = clockIdToClockSource(MOTU_CLKSRC_ADAT_9PIN); + r.push_back(s); + s = clockIdToClockSource(MOTU_CLKSRC_AES_EBU); + r.push_back(s); + + return r; +} + +bool +MotuDevice::setActiveClockSource(ClockSource s) { + debugOutput(DEBUG_LEVEL_VERBOSE, "setting clock source to id: %d\n",s.id); + + // FIXME: this could do with some error checking + return setClockCtrlRegister(-1, s.id); +} + +FFADODevice::ClockSource +MotuDevice::getActiveClockSource() { + ClockSource s; + quadlet_t clock_id = ReadRegister(MOTU_REG_CLK_CTRL) & MOTU_CLKSRC_MASK; + s = clockIdToClockSource(clock_id); + s.active = true; + return s; +} + +bool +MotuDevice::lock() { + + return true; +} + + +bool +MotuDevice::unlock() { + + return true; +} + +void +MotuDevice::showDevice() +{ + debugOutput(DEBUG_LEVEL_VERBOSE, + "%s %s at node %d\n", m_model->vendor_name, m_model->model_name, + getNodeId()); +} + +bool +MotuDevice::prepare() { + + int samp_freq = getSamplingFrequency(); + unsigned int optical_in_mode = getOpticalMode(MOTU_DIR_IN); + unsigned int optical_out_mode = getOpticalMode(MOTU_DIR_OUT); + unsigned int event_size_in = getEventSize(MOTU_DIR_IN); + unsigned int event_size_out= getEventSize(MOTU_DIR_OUT); + + debugOutput(DEBUG_LEVEL_NORMAL, "Preparing MotuDevice...\n" ); + + // Allocate bandwidth if not previously done. + // FIXME: The bandwidth allocation calculation can probably be + // refined somewhat since this is currently based on a rudimentary + // understanding of the ieee1394 iso protocol. + // Currently we assume the following. + // * Ack/iso gap = 0.05 us + // * DATA_PREFIX = 0.16 us + // * DATA_END = 0.26 us + // These numbers are the worst-case figures given in the ieee1394 + // standard. This gives approximately 0.5 us of overheads per packet - + // around 25 bandwidth allocation units (from the ieee1394 standard 1 + // bandwidth allocation unit is 125/6144 us). We further assume the + // MOTU is running at S400 (which it should be) so one allocation unit + // is equivalent to 1 transmitted byte; thus the bandwidth allocation + // required for the packets themselves is just the size of the packet. + // We used to allocate based on the maximum packet size (1160 bytes at + // 192 kHz for the traveler) but now do this based on the actual device + // state by utilising the result from getEventSize() and remembering + // that each packet has an 8 byte CIP header. Note that bandwidth is + // allocated on a *per stream* basis - it must be allocated for both the + // transmit and receive streams. While most MOTU modules are close to + // symmetric in terms of the number of in/out channels there are + // exceptions, so we deal with receive and transmit bandwidth separately. + signed int n_events_per_packet = samp_freq<=48000?8:(samp_freq<=96000?16:32); + m_rx_bandwidth = 25 + (n_events_per_packet*event_size_in); + m_tx_bandwidth = 25 + (n_events_per_packet*event_size_out); + + // Assign iso channels if not already done + if (m_iso_recv_channel < 0) + m_iso_recv_channel = get1394Service().allocateIsoChannelGeneric(m_rx_bandwidth); + + if (m_iso_send_channel < 0) + m_iso_send_channel = get1394Service().allocateIsoChannelGeneric(m_tx_bandwidth); + + debugOutput(DEBUG_LEVEL_VERBOSE, "recv channel = %d, send channel = %d\n", + m_iso_recv_channel, m_iso_send_channel); + + if (m_iso_recv_channel<0 || m_iso_send_channel<0) { + // be nice and deallocate + if (m_iso_recv_channel >= 0) + get1394Service().freeIsoChannel(m_iso_recv_channel); + if (m_iso_send_channel >= 0) + get1394Service().freeIsoChannel(m_iso_send_channel); + + debugFatal("Could not allocate iso channels!\n"); + return false; + } + + m_receiveProcessor=new Streaming::MotuReceiveStreamProcessor(*this, event_size_in); + + // The first thing is to initialize the processor. This creates the + // data structures. + if(!m_receiveProcessor->init()) { + debugFatal("Could not initialize receive processor!\n"); + return false; + } + m_receiveProcessor->setVerboseLevel(getDebugLevel()); + + // Now we add ports to the processor + debugOutput(DEBUG_LEVEL_VERBOSE,"Adding ports to receive processor\n"); + + char *buff; + Streaming::Port *p=NULL; + + // retrieve the ID + std::string id=std::string("dev?"); + if(!getOption("id", id)) { + debugWarning("Could not retrieve id parameter, defauling to 'dev?'\n"); + } + + // Add audio capture ports + if (!addDirPorts(Streaming::Port::E_Capture, samp_freq, optical_in_mode)) { + return false; + } + + // Add MIDI port. The MOTU only has one MIDI input port, with each + // MIDI byte sent using a 3 byte sequence starting at byte 4 of the + // event data. + asprintf(&buff,"%s_cap_MIDI0",id.c_str()); + p = new Streaming::MotuMidiPort(*m_receiveProcessor, buff, + Streaming::Port::E_Capture, 4); + if (!p) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Skipped port %s\n", buff); + } + free(buff); + + // example of adding an control port: +// asprintf(&buff,"%s_cap_%s",id.c_str(),"myportnamehere"); +// p=new Streaming::MotuControlPort( +// buff, +// Streaming::Port::E_Capture, +// 0 // you can add all other port specific stuff you +// // need to pass by extending MotuXXXPort and MotuPortInfo +// ); +// free(buff); +// +// if (!p) { +// debugOutput(DEBUG_LEVEL_VERBOSE, "Skipped port %s\n",buff); +// } else { +// +// if (!m_receiveProcessor->addPort(p)) { +// debugWarning("Could not register port with stream processor\n"); +// return false; +// } else { +// debugOutput(DEBUG_LEVEL_VERBOSE, "Added port %s\n",buff); +// } +// } + + // Do the same for the transmit processor + m_transmitProcessor=new Streaming::MotuTransmitStreamProcessor(*this, event_size_out); + + m_transmitProcessor->setVerboseLevel(getDebugLevel()); + + if(!m_transmitProcessor->init()) { + debugFatal("Could not initialize transmit processor!\n"); + return false; + } + + // Now we add ports to the processor + debugOutput(DEBUG_LEVEL_VERBOSE,"Adding ports to transmit processor\n"); + + // Add audio playback ports + if (!addDirPorts(Streaming::Port::E_Playback, samp_freq, optical_out_mode)) { + return false; + } + + // Add MIDI port. The MOTU only has one output MIDI port, with each + // MIDI byte transmitted using a 3 byte sequence starting at byte 4 + // of the event data. + asprintf(&buff,"%s_pbk_MIDI0",id.c_str()); + p = new Streaming::MotuMidiPort(*m_transmitProcessor, buff, + Streaming::Port::E_Capture, 4); + if (!p) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Skipped port %s\n", buff); + } + free(buff); + + // example of adding an control port: +// asprintf(&buff,"%s_pbk_%s",id.c_str(),"myportnamehere"); +// +// p=new Streaming::MotuControlPort( +// buff, +// Streaming::Port::E_Playback, +// 0 // you can add all other port specific stuff you +// // need to pass by extending MotuXXXPort and MotuPortInfo +// ); +// free(buff); +// +// if (!p) { +// debugOutput(DEBUG_LEVEL_VERBOSE, "Skipped port %s\n",buff); +// } else { +// if (!m_transmitProcessor->addPort(p)) { +// debugWarning("Could not register port with stream processor\n"); +// return false; +// } else { +// debugOutput(DEBUG_LEVEL_VERBOSE, "Added port %s\n",buff); +// } +// } + + return true; +} + +int +MotuDevice::getStreamCount() { + return 2; // one receive, one transmit +} + +Streaming::StreamProcessor * +MotuDevice::getStreamProcessorByIndex(int i) { + switch (i) { + case 0: + return m_receiveProcessor; + case 1: + return m_transmitProcessor; + default: + return NULL; + } + return 0; +} + +bool +MotuDevice::startStreamByIndex(int i) { + +quadlet_t isoctrl = ReadRegister(MOTU_REG_ISOCTRL); + + // NOTE: this assumes that you have two streams + switch (i) { + case 0: + // TODO: do the stuff that is nescessary to make the device + // receive a stream + + // Set the streamprocessor channel to the one obtained by + // the connection management + m_receiveProcessor->setChannel(m_iso_recv_channel); + + // Mask out current transmit settings of the MOTU and replace + // with new ones. Turn bit 24 on to enable changes to the + // MOTU's iso transmit settings when the iso control register + // is written. Bit 23 enables iso transmit from the MOTU. + isoctrl &= 0xff00ffff; + isoctrl |= (m_iso_recv_channel << 16); + isoctrl |= 0x00c00000; + WriteRegister(MOTU_REG_ISOCTRL, isoctrl); + break; + case 1: + // TODO: do the stuff that is nescessary to make the device + // transmit a stream + + // Set the streamprocessor channel to the one obtained by + // the connection management + m_transmitProcessor->setChannel(m_iso_send_channel); + + // Mask out current receive settings of the MOTU and replace + // with new ones. Turn bit 31 on to enable changes to the + // MOTU's iso receive settings when the iso control register + // is written. Bit 30 enables iso receive by the MOTU. + isoctrl &= 0x00ffffff; + isoctrl |= (m_iso_send_channel << 24); + isoctrl |= 0xc0000000; + WriteRegister(MOTU_REG_ISOCTRL, isoctrl); + break; + + default: // Invalid stream index + return false; + } + + return true; +} + +bool +MotuDevice::stopStreamByIndex(int i) { + +quadlet_t isoctrl = ReadRegister(MOTU_REG_ISOCTRL); + + // TODO: connection management: break connection + // cfr the start function + + // NOTE: this assumes that you have two streams + switch (i) { + case 0: + // Turn bit 22 off to disable iso send by the MOTU. Turn + // bit 23 on to enable changes to the MOTU's iso transmit + // settings when the iso control register is written. + isoctrl &= 0xffbfffff; + isoctrl |= 0x00800000; + WriteRegister(MOTU_REG_ISOCTRL, isoctrl); + break; + case 1: + // Turn bit 30 off to disable iso receive by the MOTU. Turn + // bit 31 on to enable changes to the MOTU's iso receive + // settings when the iso control register is written. + isoctrl &= 0xbfffffff; + isoctrl |= 0x80000000; + WriteRegister(MOTU_REG_ISOCTRL, isoctrl); + break; + + default: // Invalid stream index + return false; + } + + return true; +} + +signed int MotuDevice::getIsoRecvChannel(void) { + return m_iso_recv_channel; +} + +signed int MotuDevice::getIsoSendChannel(void) { + return m_iso_send_channel; +} + +unsigned int MotuDevice::getOpticalMode(unsigned int dir) { + unsigned int reg = ReadRegister(MOTU_REG_ROUTE_PORT_CONF); + +debugOutput(DEBUG_LEVEL_VERBOSE, "optical mode: %x %x %x %x\n",dir, reg, reg & MOTU_OPTICAL_IN_MODE_MASK, +reg & MOTU_OPTICAL_OUT_MODE_MASK); + + if (dir == MOTU_DIR_IN) + return (reg & MOTU_OPTICAL_IN_MODE_MASK) >> 8; + else + return (reg & MOTU_OPTICAL_OUT_MODE_MASK) >> 10; +} + +signed int MotuDevice::setOpticalMode(unsigned int dir, unsigned int mode) { + unsigned int reg = ReadRegister(MOTU_REG_ROUTE_PORT_CONF); + unsigned int opt_ctrl = 0x0000002; + + /* THe 896HD doesn't have an SPDIF/TOSLINK optical mode, so don't try to + * set it + */ + if (m_motu_model==MOTU_MODEL_896HD && mode==MOTU_OPTICAL_MODE_TOSLINK) + return -1; + + // Set up the optical control register value according to the current + // optical port modes. At this stage it's not completely understood + // what the "Optical control" register does, so the values it's set to + // are more or less "magic" numbers. + if (reg & MOTU_OPTICAL_IN_MODE_MASK != (MOTU_OPTICAL_MODE_ADAT<<8)) + opt_ctrl |= 0x00000080; + if (reg & MOTU_OPTICAL_OUT_MODE_MASK != (MOTU_OPTICAL_MODE_ADAT<<10)) + opt_ctrl |= 0x00000040; + + if (mode & MOTU_DIR_IN) { + reg &= ~MOTU_OPTICAL_IN_MODE_MASK; + reg |= (mode << 8) & MOTU_OPTICAL_IN_MODE_MASK; + if (mode != MOTU_OPTICAL_MODE_ADAT) + opt_ctrl |= 0x00000080; + else + opt_ctrl &= ~0x00000080; + } + if (mode & MOTU_DIR_OUT) { + reg &= ~MOTU_OPTICAL_OUT_MODE_MASK; + reg |= (mode <<10) & MOTU_OPTICAL_OUT_MODE_MASK; + if (mode != MOTU_OPTICAL_MODE_ADAT) + opt_ctrl |= 0x00000040; + else + opt_ctrl &= ~0x00000040; + } + + // FIXME: there seems to be more to it than this, but for + // the moment at least this seems to work. + WriteRegister(MOTU_REG_ROUTE_PORT_CONF, reg); + return WriteRegister(MOTU_REG_OPTICAL_CTRL, opt_ctrl); +} + +signed int MotuDevice::getEventSize(unsigned int direction) { +// +// Return the size in bytes of a single event sent to (dir==MOTU_OUT) or +// from (dir==MOTU_IN) the MOTU as part of an iso data packet. +// +// FIXME: for performance it may turn out best to calculate the event +// size in setOpticalMode and cache the result in a data field. However, +// as it stands this will not adapt to dynamic changes in sample rate - we'd +// need a setFrameRate() for that. +// +// At the very least an event consists of the SPH (4 bytes) and the control/MIDI +// bytes (6 bytes). +// Note that all audio channels are sent using 3 bytes. +signed int sample_rate = getSamplingFrequency(); +signed int optical_mode = getOpticalMode(direction); +signed int size = 4+6; + +unsigned int i; +unsigned int dir = direction==Streaming::Port::E_Capture?MOTU_DIR_IN:MOTU_DIR_OUT; +unsigned int flags = (1 << ( optical_mode + 4 )); + + if ( sample_rate > 96000 ) + flags |= MOTU_PA_RATE_4x; + else if ( sample_rate > 48000 ) + flags |= MOTU_PA_RATE_2x; + else + flags |= MOTU_PA_RATE_1x; + + for (i=0; i < DevicesProperty[m_motu_model-1].n_port_entries; i++) { + if (( DevicesProperty[m_motu_model-1].port_entry[i].port_dir & dir ) && + ( DevicesProperty[m_motu_model-1].port_entry[i].port_flags & MOTU_PA_RATE_MASK & flags ) && + ( DevicesProperty[m_motu_model-1].port_entry[i].port_flags & MOTU_PA_OPTICAL_MASK & flags )) { + size += 3; + } + } + + // Finally round size up to the next quadlet boundary + return ((size+3)/4)*4; +} +/* ======================================================================= */ + +bool MotuDevice::addPort(Streaming::StreamProcessor *s_processor, + char *name, enum Streaming::Port::E_Direction direction, + int position, int size) { +/* + * Internal helper function to add a MOTU port to a given stream processor. + * This just saves the unnecessary replication of what is essentially + * boilerplate code. Note that the port name is freed by this function + * prior to exit. + */ +Streaming::Port *p=NULL; + + p = new Streaming::MotuAudioPort(*s_processor, name, direction, position, size); + + if (!p) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Skipped port %s\n",name); + } + free(name); + return true; +} +/* ======================================================================= */ + +bool MotuDevice::addDirPorts( + enum Streaming::Port::E_Direction direction, + unsigned int sample_rate, unsigned int optical_mode) { +/* + * Internal helper method: adds all required ports for the given direction + * based on the indicated sample rate and optical mode. + * + * Notes: currently ports are not created if they are disabled due to sample + * rate or optical mode. However, it might be better to unconditionally + * create all ports and just disable those which are not active. + */ +const char *mode_str = direction==Streaming::Port::E_Capture?"cap":"pbk"; +Streaming::StreamProcessor *s_processor; +unsigned int i; +char *buff; +unsigned int dir = direction==Streaming::Port::E_Capture?MOTU_DIR_IN:MOTU_DIR_OUT; +unsigned int flags = (1 << ( optical_mode + 4 )); + + if ( sample_rate > 96000 ) + flags |= MOTU_PA_RATE_4x; + else if ( sample_rate > 48000 ) + flags |= MOTU_PA_RATE_2x; + else + flags |= MOTU_PA_RATE_1x; + + // retrieve the ID + std::string id=std::string("dev?"); + if(!getOption("id", id)) { + debugWarning("Could not retrieve id parameter, defaulting to 'dev?'\n"); + } + + if (direction == Streaming::Port::E_Capture) { + s_processor = m_receiveProcessor; + } else { + s_processor = m_transmitProcessor; + } + + for (i=0; i < DevicesProperty[m_motu_model-1].n_port_entries; i++) { + if (( DevicesProperty[m_motu_model-1].port_entry[i].port_dir & dir ) && + ( DevicesProperty[m_motu_model-1].port_entry[i].port_flags & MOTU_PA_RATE_MASK & flags ) && + ( DevicesProperty[m_motu_model-1].port_entry[i].port_flags & MOTU_PA_OPTICAL_MASK & flags )) { + asprintf(&buff,"%s_%s_%s" , id.c_str(), mode_str, + DevicesProperty[m_motu_model-1].port_entry[i].port_name); + if (!addPort(s_processor, buff, direction, DevicesProperty[m_motu_model-1].port_entry[i].port_offset, 0)) + return false; + } + } + + return true; +} +/* ======================================================================== */ + +unsigned int MotuDevice::ReadRegister(unsigned int reg) { +/* + * Attempts to read the requested register from the MOTU. + */ + + quadlet_t quadlet; + + quadlet = 0; + // Note: 1394Service::read() expects a physical ID, not the node id + if (get1394Service().read(0xffc0 | getNodeId(), MOTU_BASE_ADDR+reg, 1, &quadlet) <= 0) { + debugError("Error doing motu read from register 0x%06x\n",reg); + } + + return CondSwapFromBus32(quadlet); +} + +signed int MotuDevice::WriteRegister(unsigned int reg, quadlet_t data) { +/* + * Attempts to write the given data to the requested MOTU register. + */ + + unsigned int err = 0; + data = CondSwapToBus32(data); + + // Note: 1394Service::write() expects a physical ID, not the node id + if (get1394Service().write(0xffc0 | getNodeId(), MOTU_BASE_ADDR+reg, 1, &data) <= 0) { + err = 1; + debugError("Error doing motu write to register 0x%06x\n",reg); + } + + SleepRelativeUsec(100); + return (err==0)?0:-1; +} + +} Index: /branches/libffado-2.0/src/motu/motu_controls.h =================================================================== --- /branches/libffado-2.0/src/motu/motu_controls.h (revision 1158) +++ /branches/libffado-2.0/src/motu/motu_controls.h (revision 1158) @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2005-2008 by Daniel Wagner + * Copyright (C) 2005-2008 by Pieter Palmers + * Copyright (C) 2008 by Jonathan Woithe + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 "debugmodule/debugmodule.h" + +#include "libcontrol/BasicElements.h" +#include "libcontrol/MatrixMixer.h" + +namespace Motu { + +class MotuDevice; + +#define MOTU_CTRL_CHANNEL_FADER 0x00000001 +#define MOTU_CTRL_CHANNEL_PAN 0x00000002 +#define MOTU_CTRL_CHANNEL_SOLO 0x00000004 +#define MOTU_CTRL_CHANNEL_MUTE 0x00000008 +#define MOTU_CTRL_MIX_FADER 0x00000100 +#define MOTU_CTRL_MIX_MUTE 0x00000200 +#define MOTU_CTRL_MIX_DEST 0x00000400 + +#define MOTU_CTRL_INPUT_TRIMGAIN 0x01000000 +#define MOTU_CTRL_INPUT_PAD 0x02000000 +#define MOTU_CTRL_INPUT_LEVEL 0x04000000 +#define MOTU_CTRL_INPUT_BOOST 0x08000000 +#define MOTU_CTRL_PHONES_SRC 0x10000000 +#define MOTU_CTRL_OPTICAL_MODE 0x20000000 + +#define MOTU_CTRL_STD_CHANNEL \ + (MOTU_CTRL_CHANNEL_FADER|MOTU_CTRL_CHANNEL_PAN|\ + MOTU_CTRL_CHANNEL_SOLO|MOTU_CTRL_CHANNEL_MUTE) + +#define MOTU_CTRL_STD_MIX \ + (MOTU_CTRL_MIX_FADER|MOTU_CTRL_MIX_MUTE|\ + MOTU_CTRL_MIX_DEST) + +#define MOTU_CTRL_TRAVELER_MIC_INPUT_CTRLS \ + (MOTU_CTRL_INPUT_TRIMGAIN|MOTU_CTRL_INPUT_PAD) +#define MOTU_CTRL_TRAVELER_LINE_INPUT_CTRLS \ + (MOTU_CTRL_INPUT_LEVEL|MOTU_CTRL_INPUT_BOOST) + +#define MOTU_CTRL_MASK_MUTE_VALUE 0x00010000 +#define MOTU_CTRL_MASK_MUTE_SETENABLE 0x01000000 +#define MOTU_CTRL_MASK_SOLO_VALUE 0x00020000 +#define MOTU_CTRL_MASK_SOLO_SETENABLE 0x02000000 + +#define MOTU_CTRL_MASK_ANA5_INPUT_LEVEL 0x00000010 +#define MOTU_CTRL_MASK_ANA6_INPUT_LEVEL 0x00000020 +#define MOTU_CTRL_MASK_ANA7_INPUT_LEVEL 0x00000040 +#define MOTU_CTRL_MASK_ANA8_INPUT_LEVEL 0x00000080 + +#define MOTU_CTRL_MODE_PAD 0x00000000 +#define MOTU_CTRL_MODE_TRIMGAIN 0x00000001 + +#define MOTU_INFO_IS_STREAMING 0x00000001 +#define MOTU_INFO_SAMPLE_RATE 0x00000002 +#define MOTU_INFO_HAS_MIC_INPUTS 0x00000003 +#define MOTU_INFO_HAS_AESEBU_INPUTS 0x00000004 +#define MOTU_INFO_HAS_SPDIF_INPUTS 0x00000005 +#define MOTU_INFO_HAS_OPTICAL_SPDIF 0x00000006 + +#define MOTU_CTRL_TRIMGAINPAD_MAX_CHANNEL 3 + +class MotuDiscreteCtrl + : public Control::Discrete +{ +public: + MotuDiscreteCtrl(MotuDevice &parent, unsigned int dev_reg); + MotuDiscreteCtrl(MotuDevice &parent, unsigned int dev_reg, + std::string name, std::string label, std::string descr); + + virtual bool setValue(int v) = 0; + virtual int getValue() = 0; + + // default implementations + virtual bool setValue(int idx, int v) + {return setValue(v);}; + virtual int getValue(int idx) + {return getValue();}; + + virtual int getMinimum() {return 0;}; + virtual int getMaximum() {return 0;}; + +protected: + MotuDevice &m_parent; + unsigned int m_register; +}; + +class MotuBinarySwitch + : public MotuDiscreteCtrl +{ +public: + MotuBinarySwitch(MotuDevice &parent, unsigned int dev_reg, + unsigned int val_mask, unsigned int setenable_mask); + MotuBinarySwitch(MotuDevice &parent, unsigned int dev_reg, + unsigned int val_mask, unsigned int setenable_mask, + std::string name, std::string label, std::string descr); + + virtual bool setValue(int v); + virtual int getValue(); + +protected: + unsigned int m_value_mask; + unsigned int m_setenable_mask; +}; + +class ChannelFader + : public MotuDiscreteCtrl +{ +public: + ChannelFader(MotuDevice &parent, unsigned int dev_reg); + ChannelFader(MotuDevice &parent, unsigned int dev_reg, + std::string name, std::string label, std::string descr); + + virtual bool setValue(int v); + virtual int getValue(); +}; + +class ChannelPan + : public MotuDiscreteCtrl +{ +public: + ChannelPan(MotuDevice &parent, unsigned int dev_reg); + ChannelPan(MotuDevice &parent, unsigned int dev_reg, + std::string name, std::string label, std::string descr); + + virtual bool setValue(int v); + virtual int getValue(); +}; + +class MixFader + : public MotuDiscreteCtrl +{ +public: + MixFader(MotuDevice &parent, unsigned int dev_reg); + MixFader(MotuDevice &parent, unsigned int dev_reg, + std::string name, std::string label, std::string descr); + + virtual bool setValue(int v); + virtual int getValue(); +}; + +class MixMute + : public MotuDiscreteCtrl +{ +public: + MixMute(MotuDevice &parent, unsigned int dev_reg); + MixMute(MotuDevice &parent, unsigned int dev_reg, + std::string name, std::string label, std::string descr); + + virtual bool setValue(int v); + virtual int getValue(); +}; + +class MixDest + : public MotuDiscreteCtrl +{ +public: + MixDest(MotuDevice &parent, unsigned int dev_reg); + MixDest(MotuDevice &parent, unsigned int dev_reg, + std::string name, std::string label, std::string descr); + + virtual bool setValue(int v); + virtual int getValue(); +}; + +class PhonesSrc + : public MotuDiscreteCtrl +{ +public: + PhonesSrc(MotuDevice &parent); + PhonesSrc(MotuDevice &parent, + std::string name, std::string label, std::string descr); + + virtual bool setValue(int v); + virtual int getValue(); +}; + +class OpticalMode + : public MotuDiscreteCtrl +{ +public: + OpticalMode(MotuDevice &parent, unsigned int dev_reg); + OpticalMode(MotuDevice &parent, unsigned int dev_reg, + std::string name, std::string label, std::string descr); + + virtual bool setValue(int v); + virtual int getValue(); +}; + +class InputGainPad + : public MotuDiscreteCtrl +{ +public: + InputGainPad(MotuDevice &parent, unsigned int channel, unsigned int mode); + InputGainPad(MotuDevice &parent, unsigned int channel, unsigned int mode, + std::string name, std::string label, std::string descr); + + virtual bool setValue(int v); + virtual int getValue(); +protected: + void validate(); + unsigned int dev_register(); + unsigned int m_mode; +}; + +class InfoElement + : public MotuDiscreteCtrl +{ +public: + InfoElement(MotuDevice &parent, unsigned infotype); + InfoElement(MotuDevice &parent, unsigned infotype, + std::string name, std::string label, std::string descr); + virtual bool setValue(int v); + virtual int getValue(); +}; + +} Index: /branches/libffado-2.0/src/ffadodevice.h =================================================================== --- /branches/libffado-2.0/src/ffadodevice.h (revision 1129) +++ /branches/libffado-2.0/src/ffadodevice.h (revision 1129) @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2005-2008 by Daniel Wagner + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef FFADODEVICE_H +#define FFADODEVICE_H + +#include "libutil/OptionContainer.h" +#include "libutil/PosixMutex.h" + +#include "libcontrol/BasicElements.h" + +#include "libieee1394/vendor_model_ids.h" + +#include +#include + +class DeviceManager; +class ConfigRom; +class Ieee1394Service; + +namespace Streaming { + class StreamProcessor; + class StreamProcessorManager; +} + +namespace Control { + class Container; +} + +/*! +@brief Base class for device support + + This class should be subclassed to implement ffado support + for a specific device. + +*/ +class FFADODevice + : public Util::OptionContainer, + public Control::Container +{ +public: + FFADODevice( DeviceManager&, std::auto_ptr< ConfigRom >( configRom ) ); + + virtual ~FFADODevice(); + + /** + * @brief Compares the GUID of two FFADODevices + * + * This function compares the GUID of two FFADODevices and returns true + * if the GUID of @ref a is larger than the GUID of @ref b . This is intended + * to be used with the STL sort() algorithm. + * + * Note that GUID's are converted to integers for this. + * + * @param a pointer to first FFADODevice + * @param b pointer to second FFADODevice + * + * @returns true if the GUID of @ref a is larger than the GUID of @ref b . + */ + static bool compareGUID( FFADODevice *a, FFADODevice *b ); + + /// Returns the 1394 service of the FFADO device + virtual Ieee1394Service& get1394Service(); + /// Returns the ConfigRom object of the device node. + virtual ConfigRom& getConfigRom() const; + + /** + * @brief Called by DeviceManager to load device model from cache. + * + * This function is called before discover in order to speed up + * system initializing. + * + * @returns true if device was cached and successfully loaded from cache + */ + virtual bool loadFromCache(); + + /** + * @brief Called by DeviceManager to allow device driver to save a cache version + * of the current configuration. + * + * @returns true if caching was successful. False doesn't mean an error just, + * the driver was unable to store the configuration + */ + virtual bool saveCache(); + + /** + * @brief This is called by the DeviceManager to create an instance of the device + * + * This function enables the FFADODevice to return a subclass of itself should that + * be needed. If we don't do this we'd need to know about the subclasses in the + * devicemanager, whilst now we don't. + * + * The function should return an instance of either the class itself or a subclass + * of itself. + * + * This should be overridden in any subclass. + * + * @return a new instance of the AvDevice type, NULL when unsuccessful + */ + static FFADODevice * createDevice( std::auto_ptr( x )); + + /** + * @brief This is called by the DeviceManager to discover & configure the device + * + * @return true if the device was discovered successfuly + */ + virtual bool discover() = 0; + + /** + * @brief Set the samping frequency + * @param samplingFrequency + * @return true if successful + */ + virtual bool setSamplingFrequency( int samplingFrequency ) = 0; + /** + * @brief get the samplingfrequency as an integer + * @return the sampling frequency as integer + */ + virtual int getSamplingFrequency( ) = 0; + + /** + * @brief sync state enum + */ + enum eSyncState { + eSS_Unknown=0, + eSS_Locked=1, + eSS_Unlocked=2, + }; + + /** + * @brief gets the devices current synchronization state + * @return the device's sync state + */ + virtual enum eSyncState getSyncState( ); + + /** + * @brief clock source types + */ + enum eClockSourceType { + eCT_Invalid, ///> invalid entry (e.g. on error) + eCT_Auto, ///> automatically select clock source + eCT_Internal, ///> internal sync (unspecified) + eCT_1394Bus, ///> Sync on the 1394 bus clock (e.g. CSP) + eCT_SytMatch, ///> SYT match on incoming audio stream + eCT_SytStream, ///> SYT match on incoming sync stream + eCT_WordClock, ///> SYT on WordClock input + eCT_SPDIF, ///> SYT on SPDIF input + eCT_ADAT, ///> SYT on ADAT input + eCT_TDIF, ///> SYT on TDIF input + eCT_AES, ///> SYT on AES input + eCT_SMPTE, ///> SMPTE clock + }; + + /** + * @brief convert the clock source type to a C string + * @return a C string describing the clock source type + */ + static const char *ClockSourceTypeToString(enum eClockSourceType); + + /** + * @brief Clock source identification struct + */ + class ClockSource { + public: + ClockSource() + : type( eCT_Invalid ) + , id( 0 ) + , valid( false ) + , active( false ) + , locked( true ) + , slipping( false ) + , description( "" ) + {}; + /// indicates the type of the clock source (e.g. eCT_ADAT) + enum eClockSourceType type; + /// indicated the id of the clock source (e.g. id=1 => clocksource is ADAT_1) + unsigned int id; + /// is the clock source valid (i.e. can be selected) at this moment? + bool valid; + /// is the clock source active at this moment? + bool active; + /// is the clock source locked? + bool locked; + /// is the clock source slipping? + bool slipping; + /// description of the clock struct (optional) + std::string description; + + bool operator==(const ClockSource& x) { + return (type == x.type) && (id == x.id); + } + }; + + typedef std::vector< ClockSource > ClockSourceVector; + typedef std::vector< ClockSource >::iterator ClockSourceVectorIterator; + + /** + * @brief Get the clocksources supported by this device + * + * This function returns a vector of ClockSource structures that contains + * one entry for every clock source supported by the device. + * + * @returns a vector of ClockSource structures + */ + virtual ClockSourceVector getSupportedClockSources() = 0; + + + /** + * @brief Sets the active clock source of this device + * + * This function sets the clock source of the device. + * + * @returns true upon success. false upon failure. + */ + virtual bool setActiveClockSource(ClockSource) = 0; + + /** + * @brief Returns the active clock source of this device + * + * This function returns the active clock source of the device. + * + * @returns the active ClockSource + */ + virtual ClockSource getActiveClockSource() = 0; + + /** + * @brief This is called by the device manager to give the device a unique ID. + * + * The purpose of this is to allow for unique port naming + * in case there are multiple identical devices on the bus. + * Some audio API's (e.g. jack) don't work properly when the + * port names are not unique. + * + * Say you have two devices having a port named OutputLeft. + * This can cause the streaming + * part to present two OutputLeft ports to the audio API, + * which won't work. This ID will allow you to construct + * the port names as 'dev1_OutputLeft' and 'dev2_OutputLeft' + * + * @note Currently this is a simple integer that is equal to + * the position of the device in the devicemanager's + * device list. Therefore it is dependant on the order + * in which the devices are detected. The side-effect + * of this is that it is dependant on the bus topology + * and history (e.g. busresets etc). This makes that + * these ID's are not fixed to a specific physical device. + * At some point, we will replaced this with a GUID based + * approach, which is tied to a physical device and is + * bus & time independant. + * + * @param id + * @return true if successful + */ + bool setId(unsigned int id); + + /** + * @brief Outputs the device configuration to stderr/stdout [debug helper] + * + * This function prints out a (detailed) description of the + * device detected, and its configuration. + */ + virtual void showDevice(); + + /** + * @brief Lock the device + * + * This is called by the streaming layer before we start manipulating + * and/or using the device. + * + * It should implement the mechanisms provided by the device to + * make sure that no other controller claims control of the device. + * + * @return true if successful, false if not + */ + virtual bool lock() = 0; + + /** + * @brief Unlock the device + * + * This is called by the streaming layer after we finish manipulating + * and/or using the device. + * + * It should implement the mechanisms provided by the device to + * give up exclusive control of the device. + * + * @return true if successful, false if not + */ + virtual bool unlock() = 0; + + /** + * @brief Enable streaming on all 'started' streams + * + * Enables the ISO streaming on all streams that are 'started' + * using startStreamByIndex. This is useful to control a 'master enable' + * function on the device. + * + * @return true if successful + */ + virtual bool enableStreaming(); + + /** + * @brief Disable streaming on all streams + * + * Disables ISO streaming on all streams. + * This is useful to control a 'master enable' + * function on the device. + * + * @return true if successful + */ + virtual bool disableStreaming(); + + /** + * @brief Prepare the device + * + * This is called by the streaming layer after the configuration + * parameters (e.g. sample rate) are set, and before + * getStreamProcessor[*] functions are called. + * + * It should be used to prepare the device's streamprocessors + * based upon the device's current configuration. Normally + * the streaming layer will not change the device's configuration + * after calling this function. + * + * @return true if successful, false if not + */ + virtual bool prepare() = 0; + + /** + * @brief Returns the number of ISO streams implemented/used by this device + * + * Most likely this is 2 streams, i.e. one transmit stream and one + * receive stream. However there are devices that implement more, for + * example BeBoB's implement 4 streams: + * - 2 audio streams (1 xmit/1 recv) + * - 2 sync streams (1 xmit/1 recv), which are an optional sync source + * for the device. + * + * @note you have to have a StreamProcessor for every stream. I.e. + * getStreamProcessorByIndex(i) should return a valid StreamProcessor + * for i=0 to i=getStreamCount()-1 + * + * @return number of streams available (both transmit and receive) + */ + virtual int getStreamCount() = 0; + + /** + * @brief Returns the StreamProcessor object for the stream with index i + * + * @note a streamprocessor returned by getStreamProcessorByIndex(i) + * cannot be the same object as one returned by + * getStreamProcessorByIndex(j) if i isn't equal to j + * @note you cannot have two streamprocessors handling the same ISO + * channel (on the same port) + * + * @param i : Stream index + * @pre @ref i smaller than getStreamCount() + * @return a StreamProcessor object if successful, NULL otherwise + */ + virtual Streaming::StreamProcessor *getStreamProcessorByIndex(int i) = 0; + + /** + * @brief starts the stream with index i + * + * This function is called by the streaming layer when this stream should + * be started, i.e. the device should start sending data or should be prepared to + * be ready to receive data. + * + * It returns the channel number that was assigned for this stream. + * Channel allocation should be done using the allocation functions provided by the + * Ieee1394Service object that is passed in the constructor. + * + * @param i : Stream index + * @pre @ref i smaller than getStreamCount() + * @return true if successful, false if not + */ + virtual bool startStreamByIndex(int i) = 0; + + /** + * @brief stops the stream with index @ref i + * + * @param i : Stream index + * @pre @ref i smaller than getStreamCount() + * @return true if successful, false if not + */ + virtual bool stopStreamByIndex(int i) = 0; + + /** + * set verbosity level + */ + virtual void setVerboseLevel(int l); + + /** + * @brief return the node id of this device + * + * @return the node id + */ + int getNodeId(); + + /** + * @brief return the nick name of this device + * + * @return string containing the name + */ + virtual std::string getNickname(); + + /** + * @brief set the nick name of this device + * @param name new nickname + * @return true if successful + */ + virtual bool setNickname(std::string name); + + /** + * @brief handle a bus reset + * + * Called whenever a bus reset is detected. Handle everything + * that has to be done to cope with a bus reset. + * + */ + // FIXME: not virtual? + void handleBusReset(); + + // the Control::Container functions + virtual std::string getName(); + virtual bool setName( std::string n ) + { return false; }; + + DeviceManager& getDeviceManager() + {return m_pDeviceManager;}; +private: + std::auto_ptr( m_pConfigRom ); + DeviceManager& m_pDeviceManager; + Control::Container* m_genericContainer; +protected: + DECLARE_DEBUG_MODULE; + Util::PosixMutex m_DeviceMutex; +}; + +#endif Index: /branches/libffado-2.0/src/SConscript =================================================================== --- /branches/libffado-2.0/src/SConscript (revision 1185) +++ /branches/libffado-2.0/src/SConscript (revision 1185) @@ -0,0 +1,294 @@ +# +# Copyright (C) 2007-2008 Arnold Krille +# Copyright (C) 2007-2008 Pieter Palmers +# +# 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 2 of the License, or +# (at your option) version 3 of the License. +# +# 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 . +# + +import os + +Import( 'env' ) + +env.AppendUnique( CPPPATH=["#/", "#/src"] ) + +libenv = env.Clone() + +ffado_source = env.Split( '\ + devicemanager.cpp \ + ffado.cpp \ + ffadodevice.cpp \ + debugmodule/debugmodule.cpp \ + DeviceStringParser.cpp \ + libavc/streamformat/avc_extended_stream_format.cpp \ + libavc/musicsubunit/avc_descriptor_music.cpp \ + libavc/musicsubunit/avc_musicsubunit.cpp \ + libavc/audiosubunit/avc_audiosubunit.cpp \ + libavc/audiosubunit/avc_function_block.cpp \ + libavc/descriptors/avc_descriptor_cmd.cpp \ + libavc/descriptors/avc_descriptor.cpp \ + libavc/general/avc_extended_subunit_info.cpp \ + libavc/general/avc_unit_info.cpp \ + libavc/general/avc_generic.cpp \ + libavc/general/avc_subunit_info.cpp \ + libavc/general/avc_connect.cpp \ + libavc/general/avc_signal_format.cpp \ + libavc/general/avc_extended_cmd_generic.cpp \ + libavc/general/avc_extended_plug_info.cpp \ + libavc/general/avc_plug_info.cpp \ + libavc/general/avc_unit.cpp \ + libavc/general/avc_subunit.cpp \ + libavc/general/avc_plug.cpp \ + libavc/general/avc_vendor_dependent_cmd.cpp \ + libavc/avc_definitions.cpp \ + libavc/ccm/avc_signal_source.cpp \ + libieee1394/ARMHandler.cpp \ + libieee1394/configrom.cpp \ + libieee1394/csr1212.c \ + libieee1394/CycleTimerHelper.cpp \ + libieee1394/ieee1394service.cpp \ + libieee1394/IEC61883.cpp \ + libieee1394/IsoHandler.cpp \ + libieee1394/IsoHandlerManager.cpp \ + libstreaming/StreamProcessorManager.cpp \ + libstreaming/util/cip.c \ + libstreaming/generic/StreamProcessor.cpp \ + libstreaming/generic/Port.cpp \ + libstreaming/generic/PortManager.cpp \ + libutil/cmd_serialize.cpp \ + libutil/DelayLockedLoop.cpp \ + libutil/IpcRingBuffer.cpp \ + libutil/PacketBuffer.cpp \ + libutil/OptionContainer.cpp \ + libutil/PosixMessageQueue.cpp \ + libutil/PosixSharedMemory.cpp \ + libutil/PosixMutex.cpp \ + libutil/PosixThread.cpp \ + libutil/ringbuffer.c \ + libutil/StreamStatistics.cpp \ + libutil/SystemTimeSource.cpp \ + libutil/TimestampedBuffer.cpp \ + libutil/Watchdog.cpp \ + libcontrol/Element.cpp \ + libcontrol/BasicElements.cpp \ + libcontrol/MatrixMixer.cpp \ + libcontrol/ClockSelect.cpp \ + libcontrol/Nickname.cpp \ +') + +if env['SERIALIZE_USE_EXPAT']: + ffado_source.append('libutil/serialize_expat.cpp') + ffado_source.append('libutil/serialize_expat_xml.cpp') +else: + ffado_source.append('libutil/serialize_libxml.cpp') + + +bebob_source = env.Split( '\ + bebob/bebob_avdevice.cpp \ + bebob/bebob_avdevice_subunit.cpp \ + bebob/bebob_avplug.cpp \ + bebob/bebob_dl_bcd.cpp \ + bebob/bebob_dl_codes.cpp \ + bebob/bebob_dl_mgr.cpp \ + bebob/bebob_functionblock.cpp \ + bebob/bebob_mixer.cpp \ + bebob/focusrite/focusrite_generic.cpp \ + bebob/focusrite/focusrite_saffire.cpp \ + bebob/focusrite/focusrite_saffirepro.cpp \ + bebob/focusrite/focusrite_cmd.cpp \ + bebob/terratec/terratec_device.cpp \ + bebob/terratec/terratec_cmd.cpp \ + bebob/edirol/edirol_fa101.cpp \ + bebob/esi/quatafire610.cpp \ + maudio/maudio_avdevice.cpp \ +' ) +bebob_pkgdata = env.Split( '\ + maudio/refdesign.xml \ + maudio/fw410.xml \ + maudio/fwap.xml \ + bebob/ffado_driver_bebob.txt \ +' ) + +genericavc_source = env.Split( '\ + genericavc/avc_avdevice.cpp \ + genericavc/avc_vendormodel.cpp \ +' ) + +genericavc_pkgdata = env.Split( '\ + genericavc/ffado_driver_genericavc.txt \ +' ) + +fireworks_source = env.Split( '\ + fireworks/fireworks_device.cpp \ + fireworks/fireworks_control.cpp \ + fireworks/fireworks_firmware.cpp \ + fireworks/efc/efc_avc_cmd.cpp \ + fireworks/efc/efc_cmd.cpp \ + fireworks/efc/efc_cmds_hardware.cpp \ + fireworks/efc/efc_cmds_hardware_ctrl.cpp \ + fireworks/efc/efc_cmds_flash.cpp \ + fireworks/efc/efc_cmds_mixer.cpp \ + fireworks/efc/efc_cmds_monitor.cpp \ + fireworks/efc/efc_cmds_ioconfig.cpp \ + fireworks/audiofire/audiofire_device.cpp \ +' ) + +fireworks_pkgdata = env.Split( '\ + fireworks/ffado_driver_fireworks.txt \ +' ) + +motu_source = env.Split( '\ + motu/motu_avdevice.cpp \ + motu/motu_controls.cpp \ + libstreaming/motu/MotuPort.cpp \ + libstreaming/motu/MotuPortInfo.cpp \ + libstreaming/motu/MotuReceiveStreamProcessor.cpp \ + libstreaming/motu/MotuTransmitStreamProcessor.cpp \ +' ) + +dice_source = env.Split( '\ + dice/dice_avdevice.cpp \ +' ) + +bounce_source = env.Split( '\ + bounce/bounce_avdevice.cpp \ + bounce/bounce_slave_avdevice.cpp \ + libstreaming/AmdtpSlaveStreamProcessor.cpp \ +' ) + +metric_halo_source = env.Split( '\ + metrichalo/mh_avdevice.cpp \ +' ) + +rme_source = env.Split( '\ + rme/rme_avdevice.cpp \ +' ) + +amdtp_source = env.Split( '\ + libstreaming/amdtp/AmdtpPort.cpp \ + libstreaming/amdtp/AmdtpPortInfo.cpp \ + libstreaming/amdtp/AmdtpReceiveStreamProcessor.cpp \ + libstreaming/amdtp/AmdtpTransmitStreamProcessor.cpp \ +' ) + + +source = ffado_source +pkgdata = [] +if env['ENABLE_BEBOB']: + libenv.AppendUnique( CCFLAGS=["-DENABLE_BEBOB"] ) + source += bebob_source + pkgdata += bebob_pkgdata +if env['ENABLE_FIREWORKS']: + libenv.AppendUnique( CCFLAGS=["-DENABLE_FIREWORKS"] ) + source += fireworks_source + pkgdata += fireworks_pkgdata +if env['ENABLE_MOTU']: + libenv.AppendUnique( CCFLAGS=["-DENABLE_MOTU"] ) + source += motu_source +if env['ENABLE_DICE']: + libenv.AppendUnique( CCFLAGS=["-DENABLE_DICE"] ) + source += dice_source +if env['ENABLE_METRIC_HALO']: + libenv.AppendUnique( CCFLAGS=["-DENABLE_METRIC_HALO"] ) + source += metric_halo_source +if env['ENABLE_RME']: + libenv.AppendUnique( CCFLAGS=["-DENABLE_RME"] ) + source += rme_source +if env['ENABLE_BOUNCE']: + libenv.AppendUnique( CCFLAGS=["-DENABLE_BOUNCE"] ) + source += bounce_source + +# The list of devices needing GENERICAVC is controlled in ../SConstruct +if env['ENABLE_GENERICAVC']: + libenv.AppendUnique( CCFLAGS=["-DENABLE_GENERICAVC"] ) + source += amdtp_source + source += genericavc_source + pkgdata += genericavc_pkgdata + +if not env.GetOption( "clean" ): + libenv.MergeFlags( "-lrt" ) + libenv.MergeFlags( env['LIBRAW1394_FLAGS'] ) + libenv.MergeFlags( env['LIBAVC1394_FLAGS'] ) + libenv.MergeFlags( env['LIBIEC61883_FLAGS'] ) + if not env['SERIALIZE_USE_EXPAT']: + libenv.MergeFlags( env['LIBXML26_FLAGS'] ) + else: + libenv.PrependUnique( LIBS=["expat"] ) + +#env1.AppendUnique( LINKFLAGS = env.Split("-Wl,-rpath $libdir -Wl,-soname -Wl,libffado.so.1 --version-info=1:0:0") ) +ffadolib = libenv.SharedLibrary( "ffado", source ) + +if libenv['BUILD_STATIC_LIB']: + ffadolib_static = libenv.StaticLibrary( "ffado", source ) + +# +# All the following tries to emulate the versioning of installed libs as seen from libtool... +# +if False: + print "Trying to emulate libtools versioned install" + libenv.Alias( "install", libenv.InstallAs( os.path.join('$libdir','libffado.so.$LIBVERSION'), ffadolib ) ) + libenv.Ignore( ffadolib, os.path.join('$libdir','libffado.so') ) + #env.Ignore( os.path.join('$libdir','libffado.so'), ffadolib ) + #env.Ignore( os.path.join('$libdir','libffado.so.0'), "install" ) + + libenv.Alias( "install", libenv.Command( + target="$libdir/libffado.so", + source=libenv['libdir']+"/libffado.so.$LIBVERSION", + action="ln -s $SOURCE $TARGET" + ) ) + libenv.Alias( "install", libenv.Command( + target="$libdir/libffado.so.%s" % str(libenv['LIBVERSION']).rsplit('.',1)[0], + source=libenv['libdir']+"/libffado.so.$LIBVERSION", + action="ln -s $SOURCE $TARGET" + ) ) + libenv.Alias( "install", libenv.Command( + target="$libdir/libffado.so.%s" % str(libenv['LIBVERSION']).rsplit('.',2)[0], + source=libenv['libdir']+"/libffado.so.$LIBVERSION", + action="ln -s $SOURCE $TARGET" + ) ) +else: + #print "Doing simple install" + libenv.Install( "$libdir", ffadolib ) + +# +# Install the pkgdata to $sharedir +# +for data in pkgdata: + libenv.Install( "$sharedir", data ) + +# +# For the debugging apps +# +env2 = libenv.Clone() +env2.PrependUnique( LIBPATH=env['build_base']+"src" ) +env2.PrependUnique( LIBS="ffado" ) + +apps = { \ + "test-debugmodule" : "debugmodule/test_debugmodule.cpp", \ + "test-dll" : "libutil/test-dll.cpp", \ + "test-unittests-util" : "libutil/unittests.cpp", \ + "test-cyclecalc" : "libieee1394/test-cyclecalc.cpp", \ +} + +installapps = [] + +for app in apps.keys(): + env2.Program( target=app, source = env.Split( apps[app] ) ) + if app.find( "test" ) == -1: + env2.Install( "$bindir", app ) + Index: /branches/libffado-2.0/src/metrichalo/mh_avdevice.h =================================================================== --- /branches/libffado-2.0/src/metrichalo/mh_avdevice.h (revision 864) +++ /branches/libffado-2.0/src/metrichalo/mh_avdevice.h (revision 864) @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef MHDEVICE_H +#define MHDEVICE_H + +#include "ffadodevice.h" + +#include "debugmodule/debugmodule.h" +#include "libavc/avc_definitions.h" + +// #include "libstreaming/mh/MHStreamProcessor.h" + +class ConfigRom; +class Ieee1394Service; + +namespace MetricHalo { + +// struct to define the supported devices +struct VendorModelEntry { + unsigned int vendor_id; + unsigned int model_id; + char *vendor_name; + char *model_name; +}; + +class MHAvDevice : public FFADODevice { +public: + MHAvDevice( Ieee1394Service& ieee1394Service, + std::auto_ptr( configRom )); + virtual ~MHAvDevice(); + + static bool probe( ConfigRom& configRom ); + static FFADODevice * createDevice( Ieee1394Service& ieee1394Service, + std::auto_ptr( configRom )); + static int getConfigurationId(); + virtual bool discover(); + + virtual void showDevice(); + + virtual bool setSamplingFrequency( int ); + virtual int getSamplingFrequency( ); + + virtual ClockSourceVector getSupportedClockSources(); + virtual bool setActiveClockSource(ClockSource); + virtual ClockSource getActiveClockSource(); + + virtual int getStreamCount(); + virtual Streaming::StreamProcessor *getStreamProcessorByIndex(int i); + + virtual bool prepare(); + virtual bool lock(); + virtual bool unlock(); + + virtual bool startStreamByIndex(int i); + virtual bool stopStreamByIndex(int i); + + signed int getIsoRecvChannel(void); + signed int getIsoSendChannel(void); + +protected: + struct VendorModelEntry *m_model; + +}; + +} + +#endif Index: /branches/libffado-2.0/src/metrichalo/mh_avdevice.cpp =================================================================== --- /branches/libffado-2.0/src/metrichalo/mh_avdevice.cpp (revision 1135) +++ /branches/libffado-2.0/src/metrichalo/mh_avdevice.cpp (revision 1135) @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#warning Metric Halo support is currently useless + +#include "metrichalo/mh_avdevice.h" + +#include "libieee1394/configrom.h" +#include "libieee1394/ieee1394service.h" + +#include "libavc/avc_definitions.h" + +#include "debugmodule/debugmodule.h" + +#include +#include +#include +#include "libutil/ByteSwap.h" +#include +#include + +#include + +namespace MetricHalo { + +// to define the supported devices +static VendorModelEntry supportedDeviceList[] = +{ + {0x00000000, 0x0000, "Metric Halo", "XXX"}, +}; + +MHAvDevice::MHAvDevice( Ieee1394Service& ieee1394Service, + std::auto_ptr( configRom )) + : FFADODevice( ieee1394Service, configRom ) + , m_model( NULL ) + +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Created MetricHalo::MHAvDevice (NodeID %d)\n", + getConfigRom().getNodeId() ); +} + +MHAvDevice::~MHAvDevice() +{ + +} + +bool +MHAvDevice::probe( ConfigRom& configRom ) +{ + unsigned int vendorId = configRom.getNodeVendorId(); + unsigned int modelId = configRom.getModelId(); + + for ( unsigned int i = 0; + i < ( sizeof( supportedDeviceList )/sizeof( VendorModelEntry ) ); + ++i ) + { + if ( ( supportedDeviceList[i].vendor_id == vendorId ) + && ( supportedDeviceList[i].model_id == modelId ) + ) + { + return true; + } + } + + return false; +} + +FFADODevice * +MHAvDevice::createDevice( Ieee1394Service& ieee1394Service, + std::auto_ptr( configRom )) +{ + return new MHAvDevice(ieee1394Service, configRom ); +} + +bool +MHAvDevice::discover() +{ + unsigned int vendorId = m_pConfigRom->getNodeVendorId(); + unsigned int modelId = m_pConfigRom->getModelId(); + + for ( unsigned int i = 0; + i < ( sizeof( supportedDeviceList )/sizeof( VendorModelEntry ) ); + ++i ) + { + if ( ( supportedDeviceList[i].vendor_id == vendorId ) + && ( supportedDeviceList[i].model_id == modelId ) + ) + { + m_model = &(supportedDeviceList[i]); + } + } + + if (m_model != NULL) { + debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n", + m_model->vendor_name, m_model->model_name); + return true; + } + + return false; +} + +int +MHAvDevice::getSamplingFrequency( ) { + return 0; +} + +FFADODevice::ClockSourceVector +MHAvDevice::getSupportedClockSources() { + FFADODevice::ClockSourceVector r; + return r; +} + +bool +MHAvDevice::setActiveClockSource(ClockSource s) { + return false; +} + +FFADODevice::ClockSource +MHAvDevice::getActiveClockSource() { + ClockSource s; + return s; +} + + +int +MHAvDevice::getConfigurationId( ) { + return 0; +} + +bool +MHAvDevice::setSamplingFrequency( int samplingFrequency ) +{ + + return false; +} + +bool +MHAvDevice::lock() { + + return true; +} + + +bool +MHAvDevice::unlock() { + + return true; +} + +void +MHAvDevice::showDevice() +{ + debugOutput(DEBUG_LEVEL_VERBOSE, + "%s %s at node %d\n", m_model->vendor_name, m_model->model_name, + getNodeId()); +} + +bool +MHAvDevice::prepare() { + + return true; +} + +int +MHAvDevice::getStreamCount() { + return 0; +} + +Streaming::StreamProcessor * +MHAvDevice::getStreamProcessorByIndex(int i) { + + return NULL; +} + +bool +MHAvDevice::startStreamByIndex(int i) { + return false; +} + +bool +MHAvDevice::stopStreamByIndex(int i) { + return false; +} + +} Index: /branches/libffado-2.0/src/DeviceStringParser.h =================================================================== --- /branches/libffado-2.0/src/DeviceStringParser.h (revision 942) +++ /branches/libffado-2.0/src/DeviceStringParser.h (revision 942) @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef __FFADO_DEVICESTRINGPARSER__ +#define __FFADO_DEVICESTRINGPARSER__ + +#include "debugmodule/debugmodule.h" + +#include +#include + +class ConfigRom; + +class DeviceStringParser { + +protected: + class DeviceString { + public: + enum eType { + eInvalid = 0, + eBusNode = 1, // old-style hw:bus,node + eGUID = 2, // GUID match + }; + + DeviceString(DeviceStringParser&); + ~DeviceString(); + + bool parse(std::string s); + bool match(ConfigRom &); + + std::string getString() {return m_String;}; + void show(); + + bool operator==(const DeviceString& x); + static bool isValidString(std::string s); + + private: + DeviceStringParser & m_Parent; + + int m_node; + int m_port; + uint64_t m_guid; + std::string m_String; + enum eType m_Type; + + DECLARE_DEBUG_MODULE_REFERENCE; + }; + +public: + DeviceStringParser(); + virtual ~DeviceStringParser(); + + int countDeviceStrings() {return m_DeviceStrings.size();}; + + bool match(ConfigRom &); + + bool parseString(std::string s); + void show(); + void setVerboseLevel(int i); + + static bool isValidString(std::string s); + +protected: + bool removeDeviceString(DeviceString *); + bool addDeviceString(DeviceString *); + bool hasDeviceString(DeviceString *); + +private: + int findDeviceString(DeviceString *); + + void pruneDuplicates(); + + typedef std::vector< DeviceString* > DeviceStringVector; + typedef std::vector< DeviceString* >::iterator DeviceStringVectorIterator; + DeviceStringVector m_DeviceStrings; + +protected: + DECLARE_DEBUG_MODULE; + +}; + +#endif /* __FFADO_DEVICESTRINGPARSER__ */ + + Index: /branches/libffado-2.0/src/debugmodule/debugmodule.h =================================================================== --- /branches/libffado-2.0/src/debugmodule/debugmodule.h (revision 1157) +++ /branches/libffado-2.0/src/debugmodule/debugmodule.h (revision 1157) @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2005-2008 by Daniel Wagner + * Copyright (C) 2005-2008 by Pieter Palmers + * + * 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 2 of the License, or + * (at your option) version 3 of the License. + * + * 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 . + * + */ + +#ifndef DEBUGMODULE_H +#define DEBUGMODULE_H + +#include "config.h" + +#include "../fbtypes.h" +#include + +#include +#include +#include + +typedef short debug_level_t; + +#define DEBUG_LEVEL_MESSAGE 0 +#define DEBUG_LEVEL_FATAL 1 +#define DEBUG_LEVEL_ERROR 2 +#define DEBUG_LEVEL_WARNING 3 +#define DEBUG_LEVEL_NORMAL 4 +#define DEBUG_LEVEL_INFO 5 +#define DEBUG_LEVEL_VERBOSE 6 +#define DEBUG_LEVEL_VERY_VERBOSE 7 +#define DEBUG_LEVEL_ULTRA_VERBOSE 8 + +/* MB_NEXT() relies on the fact that MB_BUFFERS is a power of two */ +#define MB_NEXT(index) (((index)+1) & (DEBUG_MB_BUFFERS-1)) +#define MB_BUFFERSIZE DEBUG_MAX_MESSAGE_LENGTH + +// no backtrace support when not debugging +#ifndef DEBUG + #undef DEBUG_BACKTRACE_SUPPORT + #define DEBUG_BACKTRACE_SUPPORT 0 +#endif + +// no backlog support when not debugging +#ifndef DEBUG + #undef DEBUG_BACKLOG_SUPPORT + #define DEBUG_BACKLOG_SUPPORT 0 +#endif + +// the backlog is a similar buffer as the message buffer +#define DEBUG_BACKLOG_MB_NEXT(index) (((index)+1) & (DEBUG_BACKLOG_MB_BUFFERS-1)) +#define DEBUG_BACKLOG_MIN_LEVEL DEBUG_LEVEL_VERY_VERBOSE + +#define debugFatal( format, args... ) \ + m_debugModule.print( DebugModule::eDL_Fatal, \ + __FILE__, \ + __FUNCTION__, \ + __LINE__, \ + format, \ + ##args ) +#define debugError( format, args... ) \ + m_debugModule.print( DebugModule::eDL_Error, \ + __FILE__, \ + __FUNCTION__, \ + __LINE__, \ + format, \ + ##args ) +#define debugWarning( format, args... ) \ + m_debugModule.print( DebugModule::eDL_Warning, \ + __FILE__, \ + __FUNCTION__, \ + __LINE__, \ + format, \ + ##args ) + +#define debugFatalShort( format, args... ) \ + m_debugModule.printShort( DebugModule::eDL_Fatal, \ + format, \ + ##args ) +#define debugErrorShort( format, args... ) \ + m_debugModule.printShort( DebugModule::eDL_Error, \ + format, \ + ##args ) +#define debugWarningShort( format, args... ) \ + m_debugModule.printShort( DebugModule::eDL_Warning, \ + format, \ + ##args ) + +// these are for messages that are also displayed when not compiled +// for debug +#ifdef DEBUG +#define printMessage( format, args... ) \ + m_debugModule.print( DebugModule::eDL_Message, \ + __FILE__, \ + __FUNCTION__, \ + __LINE__, \ + format, \ + ##args ) +#else +#define printMessage( format, args... ) \ + m_debugModule.printShort( DebugModule::eDL_Message, \ + format, \ + ##args ) +#endif +#define printMessageShort( format, args... ) \ + m_debugModule.printShort( DebugModule::eDL_Message, \ + format, \ + ##args ) + +#define DECLARE_DEBUG_MODULE static DebugModule m_debugModule +#define DECLARE_DEBUG_MODULE_REFERENCE DebugModule &m_debugModule +#define IMPL_DEBUG_MODULE( ClassName, RegisterName, Level ) \ + DebugModule ClassName::m_debugModule = \ + DebugModule( #RegisterName, Level ) + +#define DECLARE_GLOBAL_DEBUG_MODULE extern DebugModule m_debugModule +#define IMPL_GLOBAL_DEBUG_MODULE( RegisterName, Level ) \ + DebugModule m_debugModule = \ + DebugModule( #RegisterName, Level ) + +#define setDebugLevel( Level ) { \ + m_debugModule.setLevel( Level ); \ + } + +/* m_debugModule.print( eDL_Normal, \ + __FILE__, \ + __FUNCTION__, \ + __LINE__, \ + "Setting debug level to %d\n", \ + Level ); \ + }*/ + +#define getDebugLevel( ) \ + m_debugModule.getLevel( ) + +#define flushDebugOutput() DebugModuleManager::instance()->flush() + +#if DEBUG_BACKLOG_SUPPORT + +#define debugShowBackLog() \ + { \ + m_debugModule.print( DebugModule::eDL_Warning, \ + __FILE__, \ + __FUNCTION__, \ + __LINE__, \ + "Backlog print requested\n"); \ + DebugModuleManager::instance()->showBackLog(); \ + } +#define debugShowBackLogLines(x) \ + { \ + m_debugModule.print( DebugModule::eDL_Warning,