Index: /branches/api-cleanup/external/SConscript =================================================================== --- /branches/api-cleanup/external/SConscript (revision 742) +++ /branches/api-cleanup/external/SConscript (revision 742) @@ -0,0 +1,29 @@ +# +# 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( 'env' ) + +env = env.Copy() + +env.SConscript( dirs="dbus", exports="env" ) + Index: /branches/api-cleanup/external/dbus/tools/introspect.h =================================================================== --- /branches/api-cleanup/external/dbus/tools/introspect.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/tools/xml.cpp =================================================================== --- /branches/api-cleanup/external/dbus/tools/xml.cpp (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/tools/xml.h =================================================================== --- /branches/api-cleanup/external/dbus/tools/xml.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/tools/xml2cpp.cpp =================================================================== --- /branches/api-cleanup/external/dbus/tools/xml2cpp.cpp (revision 562) +++ /branches/api-cleanup/external/dbus/tools/xml2cpp.cpp (revision 562) @@ -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 { char type; 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/api-cleanup/external/dbus/tools/introspect.cpp =================================================================== --- /branches/api-cleanup/external/dbus/tools/introspect.cpp (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/tools/xml2cpp.h =================================================================== --- /branches/api-cleanup/external/dbus/tools/xml2cpp.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/error.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/error.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/dbus.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/dbus.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/glib-integration.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/glib-integration.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/connection.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/connection.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/introspection.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/introspection.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/interface.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/interface.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/types.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/types.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/config.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/config.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/object.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/object.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/server.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/server.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/api.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/api.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/eventloop.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/eventloop.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/util.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/util.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/pendingcall.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/pendingcall.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/refptr_impl.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/refptr_impl.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/property.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/property.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/message.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/message.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/dispatcher.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/dispatcher.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/include/dbus-c++/debug.h =================================================================== --- /branches/api-cleanup/external/dbus/include/dbus-c++/debug.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/SConscript =================================================================== --- /branches/api-cleanup/external/dbus/SConscript (revision 742) +++ /branches/api-cleanup/external/dbus/SConscript (revision 742) @@ -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.Copy() + +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/api-cleanup/external/dbus/src/glib-integration.cpp =================================================================== --- /branches/api-cleanup/external/dbus/src/glib-integration.cpp (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/connection.cpp =================================================================== --- /branches/api-cleanup/external/dbus/src/connection.cpp (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/introspection.cpp =================================================================== --- /branches/api-cleanup/external/dbus/src/introspection.cpp (revision 575) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/interface.cpp =================================================================== --- /branches/api-cleanup/external/dbus/src/interface.cpp (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/pendingcall_p.h =================================================================== --- /branches/api-cleanup/external/dbus/src/pendingcall_p.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/types.cpp =================================================================== --- /branches/api-cleanup/external/dbus/src/types.cpp (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/object.cpp =================================================================== --- /branches/api-cleanup/external/dbus/src/object.cpp (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/server.cpp =================================================================== --- /branches/api-cleanup/external/dbus/src/server.cpp (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/message_p.h =================================================================== --- /branches/api-cleanup/external/dbus/src/message_p.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/dispatcher_p.h =================================================================== --- /branches/api-cleanup/external/dbus/src/dispatcher_p.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/eventloop.cpp =================================================================== --- /branches/api-cleanup/external/dbus/src/eventloop.cpp (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/pendingcall.cpp =================================================================== --- /branches/api-cleanup/external/dbus/src/pendingcall.cpp (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/connection_p.h =================================================================== --- /branches/api-cleanup/external/dbus/src/connection_p.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/message.cpp =================================================================== --- /branches/api-cleanup/external/dbus/src/message.cpp (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/property.cpp =================================================================== --- /branches/api-cleanup/external/dbus/src/property.cpp (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/dispatcher.cpp =================================================================== --- /branches/api-cleanup/external/dbus/src/dispatcher.cpp (revision 562) +++ /branches/api-cleanup/external/dbus/src/dispatcher.cpp (revision 562) @@ -0,0 +1,240 @@ +/* + * + * 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); +} + +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/api-cleanup/external/dbus/src/debug.cpp =================================================================== --- /branches/api-cleanup/external/dbus/src/debug.cpp (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/error.cpp =================================================================== --- /branches/api-cleanup/external/dbus/src/error.cpp (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/server_p.h =================================================================== --- /branches/api-cleanup/external/dbus/src/server_p.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/src/internalerror.h =================================================================== --- /branches/api-cleanup/external/dbus/src/internalerror.h (revision 562) +++ /branches/api-cleanup/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/api-cleanup/external/dbus/README =================================================================== --- /branches/api-cleanup/external/dbus/README (revision 562) +++ /branches/api-cleanup/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/api-cleanup/LICENSE =================================================================== --- /branches/api-cleanup/LICENSE (revision 743) +++ /branches/api-cleanup/LICENSE (revision 743) @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. Index: /branches/api-cleanup/AUTHORS =================================================================== --- /branches/api-cleanup/AUTHORS (revision 742) +++ /branches/api-cleanup/AUTHORS (revision 742) @@ -0,0 +1,4 @@ +Daniel Wagner +Pieter Palmers +Jonathan Woithe +Arnold Krille Index: /branches/api-cleanup/src/libieee1394/test-cyclecalc.cpp =================================================================== --- /branches/api-cleanup/src/libieee1394/test-cyclecalc.cpp (revision 790) +++ /branches/api-cleanup/src/libieee1394/test-cyclecalc.cpp (revision 790) @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "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 + * + */ + + uint32_t now_ctr = 0x140001DA; + uint64_t now = CYCLE_TIMER_TO_TICKS(0x140001DA); + unsigned int cycle = 7968; + uint16_t syt = 0x583B; + + 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); + + uint64_t calc_ts = sytRecvToFullTicks(syt, cycle, now_ctr); + + 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/api-cleanup/src/libieee1394/CycleTimerHelper.cpp =================================================================== --- /branches/api-cleanup/src/libieee1394/CycleTimerHelper.cpp (revision 807) +++ /branches/api-cleanup/src/libieee1394/CycleTimerHelper.cpp (revision 807) @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "config.h" + +#include "CycleTimerHelper.h" +#include "ieee1394service.h" +#include "libutil/PosixThread.h" + +#define DLL_BANDWIDTH (0.01) +#define DLL_PI (3.141592653589793238) +#define DLL_SQRT2 (1.414213562373095049) +#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) + +/* +#define ENTER_CRITICAL_SECTION { \ + if (pthread_mutex_trylock(&m_compute_vars_lock) == EBUSY) { \ + debugWarning(" (%p) lock clash\n", this); \ + ENTER_CRITICAL_SECTION; \ + } \ + } +*/ +#define ENTER_CRITICAL_SECTION { \ + pthread_mutex_lock(&m_compute_vars_lock); \ + } +#define EXIT_CRITICAL_SECTION { \ + pthread_mutex_unlock(&m_compute_vars_lock); \ + } + +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_Thread ( NULL ) + , m_realtime ( false ) + , m_priority ( 0 ) +{ + 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_Thread ( NULL ) + , m_realtime ( rt ) + , m_priority ( prio ) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Create %p...\n", this); +} + +CycleTimerHelper::~CycleTimerHelper() +{ + if (m_Thread) { + m_Thread->Stop(); + delete m_Thread; + } +} + +bool +CycleTimerHelper::Start() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Start %p...\n", this); +#if IEEE1394SERVICE_USE_CYCLETIMER_DLL + m_Thread = new Util::PosixThread(this, m_realtime, m_priority, + PTHREAD_CANCEL_DEFERRED); + if(!m_Thread) { + debugFatal("No thread\n"); + return false; + } + if (m_Thread->Start() != 0) { + debugFatal("Could not start update thread\n"); + return false; + } +#endif + return true; +} + +bool +CycleTimerHelper::Init() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Initialize %p...\n", this); + pthread_mutex_init(&m_compute_vars_lock, NULL); + return true; +} + +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; +} + +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; +} + +#if IEEE1394SERVICE_USE_CYCLETIMER_DLL + +bool +CycleTimerHelper::Execute() +{ + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Execute %p...\n", this); + uint32_t cycle_timer; + uint64_t local_time; + if(!m_Parent.readCycleTimerReg(&cycle_timer, &local_time)) { + debugError("Could not read cycle timer register\n"); + return false; + } + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " read : CTR: %11lu, local: %17llu\n", + cycle_timer, local_time); + + double usecs_late; + if (m_first_run) { + usecs_late = 0.0; + 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, " First run\n"); + 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); + m_first_run = false; + } else { + + double diff = m_next_time_usecs - m_current_time_usecs; + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " usecs: local: %11llu current: %f next: %f, diff: %f\n", + local_time, m_current_time_usecs, m_next_time_usecs, diff); + + uint64_t cycle_timer_ticks = CYCLE_TIMER_TO_TICKS(cycle_timer); + usecs_late = ((double)local_time) - (m_next_time_usecs); + + // we update the x-axis values + m_current_time_usecs = m_next_time_usecs; + m_next_time_usecs = (local_time - usecs_late) + m_usecs_per_update; + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " usecs: current: %f next: %f usecs_late=%f\n", + m_current_time_usecs, m_next_time_usecs, usecs_late); + + // and the y-axis values + double diff_ticks = diffTicks(cycle_timer_ticks, (int64_t)m_next_time_ticks); + m_current_time_ticks = m_next_time_ticks; + m_next_time_ticks = addTicks((uint64_t)m_current_time_ticks, + (uint64_t)((DLL_COEFF_B * diff_ticks) + m_dll_e2)); + m_dll_e2 += DLL_COEFF_C * diff_ticks; + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " ticks: current: %f next: %f diff=%f\n", + m_current_time_ticks, m_next_time_ticks, diff_ticks); + + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " state: local: %11llu, dll_e2: %f, rate: %f\n", + local_time, m_dll_e2, getRate()); + } + + // track the average wakeup delay + m_avg_wakeup_delay += 0.01 * usecs_late; + + // FIXME: priority inversion! + ENTER_CRITICAL_SECTION; + m_current_vars.ticks = m_current_time_ticks; + m_current_vars.usecs = m_current_time_usecs; + m_current_vars.rate = getRate(); + EXIT_CRITICAL_SECTION; + + // wait for the next update period + int64_t time_to_sleep = (int64_t)m_next_time_usecs - m_Parent.getCurrentTimeAsUsecs(); + time_to_sleep -= (int64_t)m_avg_wakeup_delay; + //int64_t time_to_sleep = m_usecs_per_update; + if (time_to_sleep > 0) { + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " sleeping %lld usecs (avg delay: %f)\n", time_to_sleep, m_avg_wakeup_delay); + usleep(time_to_sleep); + } + 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; + + // reduce lock contention + ENTER_CRITICAL_SECTION; + my_vars = m_current_vars; + EXIT_CRITICAL_SECTION; + + double time_diff = now - my_vars.usecs; + double y_step_in_ticks = time_diff * my_vars.rate; + int64_t y_step_in_ticks_int = (int64_t)y_step_in_ticks; + uint64_t offset_in_ticks_int = (uint64_t)my_vars.ticks; + + if (y_step_in_ticks_int > 0) { + retval = addTicks(offset_in_ticks_int, y_step_in_ticks_int); + debugOutput(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 + debugOutput(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() +{ + return TICKS_TO_CYCLE_TIMER(getCycleTimerTicks()); +} + +uint32_t +CycleTimerHelper::getCycleTimer(uint64_t now) +{ + return TICKS_TO_CYCLE_TIMER(getCycleTimerTicks(now)); +} + +#else + +bool +CycleTimerHelper::Execute() +{ + usleep(1000*1000); + return true; +} +uint32_t +CycleTimerHelper::getCycleTimerTicks() +{ + uint32_t cycle_timer; + uint64_t local_time; + if(!m_Parent.readCycleTimerReg(&cycle_timer, &local_time)) { + debugError("Could not read cycle timer register\n"); + return 0; + } + return CYCLE_TIMER_TO_TICKS(cycle_timer); +} + +uint32_t +CycleTimerHelper::getCycleTimerTicks(uint64_t now) +{ + return getCycleTimerTicks(); +} + +uint32_t +CycleTimerHelper::getCycleTimer() +{ + uint32_t cycle_timer; + uint64_t local_time; + if(!m_Parent.readCycleTimerReg(&cycle_timer, &local_time)) { + debugError("Could not read cycle timer register\n"); + return 0; + } + return cycle_timer; +} + +uint32_t +CycleTimerHelper::getCycleTimer(uint64_t now) +{ + return getCycleTimer(); +} + +#endif + +void +CycleTimerHelper::setVerboseLevel(int l) +{ + setDebugLevel(l); +} Index: /branches/api-cleanup/src/libieee1394/csr1212.h =================================================================== --- /branches/api-cleanup/src/libieee1394/csr1212.h (revision 742) +++ /branches/api-cleanup/src/libieee1394/csr1212.h (revision 742) @@ -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 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 . + * + */ +/* + * 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/api-cleanup/src/libieee1394/cycletimer.h =================================================================== --- /branches/api-cleanup/src/libieee1394/cycletimer.h (revision 748) +++ /branches/api-cleanup/src/libieee1394/cycletimer.h (revision 748) @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +/* 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_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 >= TICKS_PER_SECOND * 128L) { + x -= TICKS_PER_SECOND * 128L; + } + +#ifdef DEBUG + if (x >= 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 = x - y; + + // the maximal difference we allow (64secs) + 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; + + debugOutput(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); + + // 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) { + 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 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; + + debugOutput(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); + + // 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 (xmt_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/api-cleanup/src/libieee1394/CycleTimerHelper.h =================================================================== --- /branches/api-cleanup/src/libieee1394/CycleTimerHelper.h (revision 752) +++ /branches/api-cleanup/src/libieee1394/CycleTimerHelper.h (revision 752) @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "libutil/Thread.h" +#include "cycletimer.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. + */ +#ifndef __CYCLETIMERTHREAD_H__ +#define __CYCLETIMERTHREAD_H__ + +#include "debugmodule/debugmodule.h" + +class Ieee1394Service; +namespace Util { + class TimeSource; + class Thread; +} + +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(); + + void setVerboseLevel(int l); + +private: + 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; + + // cached vars used for computation + struct compute_vars { + double usecs; + double ticks; + double rate; + }; + + struct compute_vars m_current_vars; + pthread_mutex_t m_compute_vars_lock; + + // Threading + Util::Thread * m_Thread; + bool m_realtime; + unsigned int m_priority; + + // debug stuff + DECLARE_DEBUG_MODULE; +}; +#endif Index: /branches/api-cleanup/src/libieee1394/ieee1394service.cpp =================================================================== --- /branches/api-cleanup/src/libieee1394/ieee1394service.cpp (revision 807) +++ /branches/api-cleanup/src/libieee1394/ieee1394service.cpp (revision 807) @@ -0,0 +1,1071 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "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 +#include + +#include + +#include +#include + +IMPL_DEBUG_MODULE( Ieee1394Service, Ieee1394Service, DEBUG_LEVEL_NORMAL ); + +Ieee1394Service::Ieee1394Service() + : m_handle( 0 ), m_resetHandle( 0 ), m_util_handle( 0 ) + , m_port( -1 ) + , m_threadRunning( false ) + , m_realtime ( false ) + , m_base_priority ( 0 ) + , m_pIsoManager( new IsoHandlerManager( *this ) ) + , m_pCTRHelper ( new CycleTimerHelper( *this, 10000 ) ) + , m_have_new_ctr_read ( false ) + , m_pTimeSource ( new Util::SystemTimeSource() ) +{ + pthread_mutex_init( &m_mutex, 0 ); + + 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_resetHandle( 0 ), m_util_handle( 0 ) + , m_port( -1 ) + , m_threadRunning( false ) + , m_realtime ( rt ) + , m_base_priority ( prio ) + , m_pIsoManager( new IsoHandlerManager( *this, rt, prio + IEEE1394SERVICE_ISOMANAGER_PRIO_INCREASE ) ) + , m_pCTRHelper ( new CycleTimerHelper( *this, 1000, rt, prio + IEEE1394SERVICE_CYCLETIMER_HELPER_PRIO_INCREASE ) ) + , m_have_new_ctr_read ( false ) + , m_pTimeSource ( new Util::SystemTimeSource() ) +{ + pthread_mutex_init( &m_mutex, 0 ); + + 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_pTimeSource; + if ( m_handle ) { + raw1394_destroy_handle( m_handle ); + } + if ( m_resetHandle ) { + raw1394_destroy_handle( m_resetHandle ); + } + if ( m_util_handle ) { + raw1394_destroy_handle( m_util_handle ); + } +} + +unsigned int +Ieee1394Service::detectNbPorts( ) +{ + raw1394handle_t tmp_handle = raw1394_new_handle(); + if ( tmp_handle == NULL ) { + debugError("Could not get libraw1394 handle.\n"); + return 0; + } + 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 0; + } + return nb_detected_ports; +} + +bool +Ieee1394Service::initialize( int port ) +{ + using namespace std; + + 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_NORMAL, "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 + IEEE1394SERVICE_ISOMANAGER_PRIO_INCREASE); + result &= m_pIsoManager->setThreadParameters(rt, priority + IEEE1394SERVICE_ISOMANAGER_PRIO_INCREASE); + } + if (m_pCTRHelper) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Switching CycleTimerHelper to (rt=%d, prio=%d)\n", + rt, priority + IEEE1394SERVICE_CYCLETIMER_HELPER_PRIO_INCREASE); + result &= m_pCTRHelper->setThreadParameters(rt, priority + IEEE1394SERVICE_CYCLETIMER_HELPER_PRIO_INCREASE); + } + return result; +} + +int +Ieee1394Service::getNodeCount() +{ + return raw1394_get_nodecount( m_handle ); +} + +nodeid_t Ieee1394Service::getLocalNodeId() { + 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(); +} + +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 = ntohl(*cycle_timer); + return true; + } else { + return false; + } + } +} + +uint64_t +Ieee1394Service::getCurrentTimeAsUsecs() { + if(m_pTimeSource) { + return m_pTimeSource->getCurrentTimeAsUsecs(); + } else { + debugError("No timesource!\n"); + return 0; + } +} + +bool +Ieee1394Service::read( fb_nodeid_t nodeId, + fb_nodeaddr_t addr, + size_t length, + fb_quadlet_t* buffer ) +{ + using namespace std; + if ( raw1394_read( m_handle, nodeId, addr, length*4, buffer ) == 0 ) { + + #ifdef DEBUG + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, + "read: node 0x%X, addr = 0x%016llX, length = %u\n", + nodeId, addr, length); + printBuffer( DEBUG_LEVEL_VERY_VERBOSE, length, buffer ); + #endif + + return true; + } else { + #ifdef DEBUG + debugError("raw1394_read failed: node 0x%X, 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 ) +{ + using namespace std; + + #ifdef DEBUG + debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"write: node 0x%X, addr = 0x%016X, 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 ) ); +} + +fb_octlet_t +Ieee1394Service::byteSwap_octlet(fb_octlet_t value) { + #if __BYTE_ORDER == __BIG_ENDIAN + return value; + #elif __BYTE_ORDER == __LITTLE_ENDIAN + fb_octlet_t value_new; + fb_quadlet_t *in_ptr=reinterpret_cast(&value); + fb_quadlet_t *out_ptr=reinterpret_cast(&value_new); + *(out_ptr+1)=htonl(*(in_ptr)); + *(out_ptr)=htonl(*(in_ptr+1)); + return value_new; + #else + #error Unknown endiannes + #endif +} + +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=byteSwap_octlet(compare_value); + swap_value=byteSwap_octlet(swap_value); + + int retval=raw1394_lock64(m_handle, nodeId, addr, RAW1394_EXTCODE_COMPARE_SWAP, + swap_value, compare_value, result); + + #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=byteSwap_octlet(*result); + + return (retval == 0); +} + +fb_quadlet_t* +Ieee1394Service::transactionBlock( fb_nodeid_t nodeId, + fb_quadlet_t* buf, + int len, + unsigned int* resp_len ) +{ + for (int i = 0; i < len; ++i) { + buf[i] = ntohl( 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] = htonl( result[i] ); + } + + return result; +} + + +bool +Ieee1394Service::transactionBlockClose() +{ + avc1394_transaction_block_close( m_handle ); + 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 ) + { + 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; + } + pthread_mutex_lock( &m_mutex ); + i = pthread_create( &m_thread, 0, rHThread, this ); + pthread_mutex_unlock( &m_mutex ); + if (i) { + debugFatal("Could not start ieee1394 service thread\n"); + return false; + } + m_threadRunning = true; + + return true; +} + +void +Ieee1394Service::stopRHThread() +{ + if ( m_threadRunning ) { + pthread_mutex_lock (&m_mutex); + pthread_cancel (m_thread); + pthread_join (m_thread, 0); + pthread_mutex_unlock (&m_mutex); + 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( Functor* functor ) +{ + debugOutput(DEBUG_LEVEL_VERBOSE, "Adding busreset handler (%p)\n", functor); + m_busResetHandlers.push_back( functor ); + return true; +} + +bool +Ieee1394Service::remBusResetHandler( 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" ); + + 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" ); + + struct ChannelInfo cinfo; + + int c = -1; + int bandwidth=1; + + // 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 ); + + 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; + 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 ntohl(buffer); +} + +void +Ieee1394Service::setVerboseLevel(int l) +{ + if (m_pIsoManager) m_pIsoManager->setVerboseLevel(l); + if (m_pCTRHelper) m_pCTRHelper->setVerboseLevel(l); + setDebugLevel(l); + debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); +} + +void +Ieee1394Service::show() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Port: %d\n", getPort() ); + debugOutput( DEBUG_LEVEL_VERBOSE, " Name: %s\n", getPortName().c_str() ); + debugOutputShort( DEBUG_LEVEL_NORMAL, "Iso handler info:\n"); + if (m_pIsoManager) m_pIsoManager->dumpInfo(); +} Index: /branches/api-cleanup/src/libieee1394/IEC61883.cpp =================================================================== --- /branches/api-cleanup/src/libieee1394/IEC61883.cpp (revision 742) +++ /branches/api-cleanup/src/libieee1394/IEC61883.cpp (revision 742) @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "IEC61883.h" + +IMPL_DEBUG_MODULE( IEC61883, IEC61883, DEBUG_LEVEL_NORMAL ); + +IEC61883::IEC61883() { + +} + +IEC61883::~IEC61883() { + +} Index: /branches/api-cleanup/src/libieee1394/ARMHandler.cpp =================================================================== --- /branches/api-cleanup/src/libieee1394/ARMHandler.cpp (revision 742) +++ /branches/api-cleanup/src/libieee1394/ARMHandler.cpp (revision 742) @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "ARMHandler.h" + +IMPL_DEBUG_MODULE( ARMHandler, ARMHandler, DEBUG_LEVEL_VERBOSE); +/** + * @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/api-cleanup/src/libieee1394/ieee1394service.h =================================================================== --- /branches/api-cleanup/src/libieee1394/ieee1394service.h (revision 752) +++ /branches/api-cleanup/src/libieee1394/ieee1394service.h (revision 752) @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * Copyright (C) 2005-2007 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 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 . + * + */ + +#ifndef FFADO_IEEE1394SERVICE_H +#define FFADO_IEEE1394SERVICE_H + +#include "fbtypes.h" +#include "threads.h" + +#include "debugmodule/debugmodule.h" + +#include "IEC61883.h" + +#include +#include + +#include +#include + +class ARMHandler; +class IsoHandlerManager; +class CycleTimerHelper; + +namespace Util { + class TimeSource; +} + +class Ieee1394Service : public IEC61883 { +public: + Ieee1394Service(); + Ieee1394Service(bool rt, int prio); + ~Ieee1394Service(); + + bool initialize( int port ); + bool setThreadParameters(bool rt, int priority); + /** + * @brief get number of ports (firewire adapters) in this machine + * + * @return the number of ports + */ + static unsigned 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 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(); + + raw1394handle_t getHandle() {return m_handle;}; + + int getVerboseLevel(); + + bool addBusResetHandler( Functor* functor ); + bool remBusResetHandler( Functor* functor ); + + /** + * @brief get the current generation + * + * @return the current generation + **/ + unsigned int getGeneration() { + 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; + 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; + pthread_mutex_t m_mutex; + bool m_threadRunning; + + bool m_realtime; + int m_base_priority; + + IsoHandlerManager* m_pIsoManager; + CycleTimerHelper* m_pCTRHelper; + bool m_have_new_ctr_read; + + // the time source + Util::TimeSource* m_pTimeSource; + + typedef std::vector< 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; + + fb_octlet_t byteSwap_octlet(fb_octlet_t value); + +public: + void setVerboseLevel(int l); + void show(); +private: + DECLARE_DEBUG_MODULE; +}; + +#endif // FFADO_IEEE1394SERVICE_H Index: /branches/api-cleanup/src/libieee1394/IEC61883.h =================================================================== --- /branches/api-cleanup/src/libieee1394/IEC61883.h (revision 742) +++ /branches/api-cleanup/src/libieee1394/IEC61883.h (revision 742) @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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/api-cleanup/src/libieee1394/vendor_model_ids.h =================================================================== --- /branches/api-cleanup/src/libieee1394/vendor_model_ids.h (revision 742) +++ /branches/api-cleanup/src/libieee1394/vendor_model_ids.h (revision 742) @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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/api-cleanup/src/libieee1394/ARMHandler.h =================================================================== --- /branches/api-cleanup/src/libieee1394/ARMHandler.h (revision 742) +++ /branches/api-cleanup/src/libieee1394/ARMHandler.h (revision 742) @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2007 by Pieter Palmers + * Copyright (C) 2005-2007 by Daniel Wagner + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#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;}; + nodeaddr_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/api-cleanup/src/libieee1394/configrom.cpp =================================================================== --- /branches/api-cleanup/src/libieee1394/configrom.cpp (revision 742) +++ /branches/api-cleanup/src/libieee1394/configrom.cpp (revision 742) @@ -0,0 +1,616 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "configrom.h" +#include "ieee1394service.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("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("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 + } + + 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 Glib::ustring +ConfigRom::getGuidString() const +{ + char* buf; + asprintf( &buf, "%08x%08x", + ( unsigned int ) ( getGuid() >> 32 ), + ( unsigned int ) ( getGuid() & 0xffffffff ) ); + Glib::ustring result = buf; + free( buf ); + return result; +} + +const Glib::ustring +ConfigRom::getModelName() const +{ + return m_modelName; +} + +const Glib::ustring +ConfigRom::getVendorName() const +{ + 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::printConfigRom() 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() ); +} + +unsigned short +ConfigRom::getAsyMaxPayload() const +{ + // XXX use pow instead? + return 1 << ( m_maxRec + 1 ); +} + +bool +ConfigRom::serialize( Glib::ustring 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", Glib::ustring( m_vendorName ) ); + result &= ser.write( path + "m_modelName", Glib::ustring( 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( Glib::ustring 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/api-cleanup/src/libieee1394/IsoHandlerManager.cpp =================================================================== --- /branches/api-cleanup/src/libieee1394/IsoHandlerManager.cpp (revision 807) +++ /branches/api-cleanup/src/libieee1394/IsoHandlerManager.cpp (revision 807) @@ -0,0 +1,683 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "config.h" +#include "IsoHandlerManager.h" +#include "ieee1394service.h" +#include "IsoHandler.h" +#include "libstreaming/generic/StreamProcessor.h" + +#include "libutil/Atomic.h" + +#include "libutil/PosixThread.h" + +#include + +IMPL_DEBUG_MODULE( IsoHandlerManager, IsoHandlerManager, DEBUG_LEVEL_NORMAL ); + +using namespace Streaming; + +IsoHandlerManager::IsoHandlerManager(Ieee1394Service& service) + : m_State(E_Created) + , m_service( service ) + , m_realtime(false), m_priority(0) + , m_Thread ( 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_Thread ( NULL ) +{} + +IsoHandlerManager::~IsoHandlerManager() +{ + stopHandlers(); + pruneHandlers(); + if(m_IsoHandlers.size() > 0) { + debugError("Still some handlers in use\n"); + } + if (m_Thread) { + m_Thread->Stop(); + delete m_Thread; + } +} + +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; + bool result = true; + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + result &= (*it)->setThreadParameters(m_realtime, m_priority); + } + + if (m_Thread) { + if (m_realtime) { + m_Thread->AcquireRealTime(m_priority); + } else { + m_Thread->DropRealTime(); + } + } + + return result; +} + +/** + * Update the shadow variables. Should only be called from + * the iso handler iteration thread + */ +void +IsoHandlerManager::updateShadowVars() +{ + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "updating shadow vars...\n"); + unsigned int i; + m_poll_nfds_shadow = m_IsoHandlers.size(); + if(m_poll_nfds_shadow > ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT) { + debugWarning("Too much ISO Handlers in manager...\n"); + m_poll_nfds_shadow = ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT; + } + for (i = 0; i < m_poll_nfds_shadow; i++) { + IsoHandler *h = m_IsoHandlers.at(i); + assert(h); + m_IsoHandler_map_shadow[i] = h; + + m_poll_fds_shadow[i].fd = h->getFileDescriptor(); + m_poll_fds_shadow[i].revents = 0; + if (h->isEnabled()) { + m_poll_fds_shadow[i].events = POLLIN; + } else { + m_poll_fds_shadow[i].events = 0; + } + } + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " updated shadow vars...\n"); +} + +bool +IsoHandlerManager::Init() { + debugOutput( DEBUG_LEVEL_VERBOSE, "%p: Init thread...\n", this); + bool result = true; + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + result &= (*it)->Init(); + } + return result; +} + +bool +IsoHandlerManager::Execute() { + int err; + unsigned int i; + + unsigned int m_poll_timeout = 100; + + updateShadowVars(); + // bypass if no handlers are registered + if (m_poll_nfds_shadow == 0) { + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "bypass iterate since no handlers registered\n"); + usleep(m_poll_timeout * 1000); + return true; + } + + // Use a shadow map of the fd's such that the poll call is not in a critical section + uint64_t poll_enter = m_service.getCurrentTimeAsUsecs(); + err = poll (m_poll_fds_shadow, m_poll_nfds_shadow, m_poll_timeout); + uint64_t poll_exit = m_service.getCurrentTimeAsUsecs(); + + if (err == -1) { + if (errno == EINTR) { + return true; + } + debugFatal("poll error: %s\n", strerror (errno)); + return false; + } + + int nb_rcv = 0; + int nb_xmit = 0; + uint64_t iter_enter = m_service.getCurrentTimeAsUsecs(); + for (i = 0; i < m_poll_nfds_shadow; i++) { + if(m_poll_fds_shadow[i].revents) { + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "received events: %08X for (%p)\n", + m_poll_fds_shadow[i].revents, m_IsoHandler_map_shadow[i]); + } + if (m_poll_fds_shadow[i].revents & POLLERR) { + debugWarning("error on fd for %d\n",i); + } + + if (m_poll_fds_shadow[i].revents & POLLHUP) { + debugWarning("hangup on fd for %d\n",i); + } + + if(m_poll_fds_shadow[i].revents & (POLLIN)) { + if (m_IsoHandler_map_shadow[i]->getType() == IsoHandler::eHT_Receive) { + m_IsoHandler_map_shadow[i]->iterate(); + nb_rcv++; + } else { + // only iterate the xmit handler if it makes sense + if(m_IsoHandler_map_shadow[i]->tryWaitForClient()) { + m_IsoHandler_map_shadow[i]->iterate(); + nb_xmit++; + } + } + } + } + uint64_t iter_exit = m_service.getCurrentTimeAsUsecs(); + + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " poll took %6lldus, iterate took %6lldus, iterated (R: %2d, X: %2d) handlers\n", + poll_exit-poll_enter, iter_exit-iter_enter, + nb_rcv, nb_xmit); + + 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; + } + +#if ISOHANDLER_PER_HANDLER_THREAD + // the IsoHandlers will create their own thread. +#else + // create a thread to iterate our handlers + debugOutput( DEBUG_LEVEL_VERBOSE, "Start thread for %p...\n", this); + m_Thread = new Util::PosixThread(this, m_realtime, m_priority, + PTHREAD_CANCEL_DEFERRED); + if(!m_Thread) { + debugFatal("No thread\n"); + return false; + } + if (m_Thread->Start() != 0) { + debugFatal("Could not start update thread\n"); + return false; + } +#endif + + m_State=E_Running; + return true; +} + +bool +IsoHandlerManager::disable(IsoHandler *h) { + bool result; + int i=0; + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Disable on IsoHandler %p\n", h); + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + if ((*it) == h) { + result = h->disable(); + 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_VERY_VERBOSE, "Enable on IsoHandler %p\n", h); + for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin(); + it != m_IsoHandlers.end(); + ++it ) + { + if ((*it) == h) { + result = h->enable(); + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " enabled\n"); + return result; + } + i++; + } + debugError("Handler not found\n"); + return false; +} + +bool IsoHandlerManager::registerHandler(IsoHandler *handler) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n"); + assert(handler); + handler->setVerboseLevel(getDebugLevel()); + m_IsoHandlers.push_back(handler); + updateShadowVars(); + 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); + updateShadowVars(); + 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; + } + + max_packet_size = page_size; + unsigned int irq_interval = packets_per_period / MINIMUM_INTERRUPTS_PER_PERIOD; + if(irq_interval <= 0) irq_interval=1; + + // the SP specifies how many packets to ISO-buffer + int buffers = stream->getNbPacketsIsoXmitBuffer(); + + 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; + } + + // set the handler's thread parameters + // receive handlers have lower priority than the client thread + // since they have ISO side buffering + // xmit handlers have higher priority since we want client side + // frames to be put into the ISO buffers ASAP + int thread_prio; + if (stream->getType()==StreamProcessor::ePT_Receive) { + thread_prio = m_priority - 1; + if (thread_prio < THREAD_MIN_RTPRIO) thread_prio = THREAD_MIN_RTPRIO; + } else { + thread_prio = m_priority + 1; + if (thread_prio > THREAD_MAX_RTPRIO) thread_prio = THREAD_MAX_RTPRIO; + } + + if(!h->setThreadParameters(m_realtime, thread_prio)) { + debugFatal("Could not set handler thread parameters\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)) { + bool result; + debugOutput( DEBUG_LEVEL_VERBOSE, " stopping handler %p for stream %p\n", *it, stream); + result = (*it)->disable(); + if(!result) { + debugOutput( DEBUG_LEVEL_VERBOSE, " could not disable 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)) { + bool result; + debugOutput( DEBUG_LEVEL_VERBOSE, " starting handler %p for stream %p\n", *it, stream); + result = (*it)->enable(cycle); + if(!result) { + debugOutput( DEBUG_LEVEL_VERBOSE, " could not enable 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; + } + } + + 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); + } +} + +void IsoHandlerManager::dumpInfo() { + 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(); + } +} + +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/api-cleanup/src/libieee1394/configrom.h =================================================================== --- /branches/api-cleanup/src/libieee1394/configrom.h (revision 742) +++ /branches/api-cleanup/src/libieee1394/configrom.h (revision 742) @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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 Glib::ustring getGuidString() const; + const Glib::ustring getModelName() const; + const Glib::ustring 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( Glib::ustring path, Util::IOSerialize& ser ); + static ConfigRom* deserialize( Glib::ustring path, + Util::IODeserialize& deser, + Ieee1394Service& ieee1394Service ); + + 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; + Glib::ustring m_vendorName; + Glib::ustring 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/api-cleanup/src/libieee1394/IsoHandlerManager.h =================================================================== --- /branches/api-cleanup/src/libieee1394/IsoHandlerManager.h (revision 796) +++ /branches/api-cleanup/src/libieee1394/IsoHandlerManager.h (revision 796) @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +#ifndef __FFADO_ISOHANDLERMANAGER__ +#define __FFADO_ISOHANDLERMANAGER__ + +#include "config.h" + +#include "debugmodule/debugmodule.h" + +#include "libutil/Thread.h" + +#include +#include + +#include + +class Ieee1394Service; + +class IsoHandler; +namespace Streaming { + class StreamProcessor; + class StreamProcessorManager; + typedef std::vector StreamProcessorVector; + typedef std::vector::iterator StreamProcessorVectorIterator; +} + +typedef std::vector IsoHandlerVector; +typedef std::vector::iterator IsoHandlerVectorIterator; + +/*! +\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 : public Util::RunnableInterface +{ + friend class Streaming::StreamProcessorManager; + public: + bool Init(); + bool Execute(); + void updateShadowVars(); + private: + // shadow variables + 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; + + 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 + ///> 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;}; + + // 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; + + // thread params for the handler threads + bool m_realtime; + int m_priority; + Util::Thread * m_Thread; + + // debug stuff + DECLARE_DEBUG_MODULE; + +}; + +#endif /* __FFADO_ISOHANDLERMANAGER__ */ + + + Index: /branches/api-cleanup/src/libieee1394/IsoHandler.cpp =================================================================== --- /branches/api-cleanup/src/libieee1394/IsoHandler.cpp (revision 807) +++ /branches/api-cleanup/src/libieee1394/IsoHandler.cpp (revision 807) @@ -0,0 +1,595 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "config.h" + +#include "IsoHandler.h" +#include "ieee1394service.h" + +#include "libstreaming/generic/StreamProcessor.h" +#include "libutil/PosixThread.h" + +#include +#include +#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 dropped) { + + IsoHandler *xmitHandler = static_cast(raw1394_get_userdata(handle)); + assert(xmitHandler); + + return xmitHandler->getPacket(data, length, tag, sy, cycle, dropped); +} + +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 dropped) { + + IsoHandler *recvHandler = static_cast(raw1394_get_userdata(handle)); + assert(recvHandler); + + return recvHandler->putPacket(data, length, channel, tag, sy, cycle, dropped); +} + +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_packetcount( 0 ) + , m_dropped( 0 ) + , m_Client( 0 ) + , m_poll_timeout( 100 ) + , m_realtime ( false ) + , m_priority ( 0 ) + , m_Thread ( NULL ) + , m_speed( RAW1394_ISO_SPEED_400 ) + , m_prebuffers( 0 ) + , m_State( E_Created ) +{ +} + +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_packetcount( 0 ) + , m_dropped( 0 ) + , m_Client( 0 ) + , m_poll_timeout( 100 ) + , m_realtime ( false ) + , m_priority ( 0 ) + , m_Thread ( NULL ) + , m_speed( RAW1394_ISO_SPEED_400 ) + , m_prebuffers( 0 ) + , m_State( E_Created ) +{ +} + +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_packetcount( 0 ) + , m_dropped( 0 ) + , m_Client( 0 ) + , m_poll_timeout( 100 ) + , m_realtime ( false ) + , m_priority ( 0 ) + , m_Thread ( NULL ) + , m_speed( speed ) + , m_prebuffers( 0 ) + , m_State( E_Created ) +{ +} + +IsoHandler::~IsoHandler() { + if (m_Thread) { + m_Thread->Stop(); + delete m_Thread; + } +// 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::Init() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "%p: Init thread...\n", this); + m_poll_fd.fd = getFileDescriptor(); + m_poll_fd.revents = 0; + if (isEnabled()) { + m_poll_fd.events = POLLIN; + } else { + m_poll_fd.events = 0; + } + return true; +} + +bool +IsoHandler::waitForClient() +{ + debugOutput(DEBUG_LEVEL_VERBOSE, "waiting...\n"); + if(m_Client) { + bool result = m_Client->waitForSignal(); + debugOutput(DEBUG_LEVEL_VERBOSE, " returns %d\n", result); + return result; + } else { + debugOutput(DEBUG_LEVEL_VERBOSE, " no client\n"); + } + return false; +} + +bool +IsoHandler::tryWaitForClient() +{ + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "waiting...\n"); + if(m_Client) { + bool result = m_Client->tryWaitForSignal(); + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " returns %d\n", result); + return result; + } else { + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " no client\n"); + } + return false; +} + +bool +IsoHandler::Execute() +{ + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "%p: Execute thread...\n", this); + + // bypass if not running + if (m_State != E_Running) { + debugOutput( DEBUG_LEVEL_VERBOSE, "%p: not polling since not running...\n", this); + usleep(m_poll_timeout * 1000); + debugOutput( DEBUG_LEVEL_VERBOSE, "%p: done sleeping...\n", this); + return true; + } + + // wait for the availability of frames in the client + // (blocking for transmit handlers) +#ifdef DEBUG + if (getType() == eHT_Transmit) { + debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) Waiting for Client to signal frame availability...\n", this); + } +#endif + if (getType() == eHT_Receive || waitForClient()) { + +#if ISOHANDLER_USE_POLL + uint64_t poll_enter = m_manager.get1394Service().getCurrentTimeAsUsecs(); + err = poll(&m_poll_fd, 1, m_poll_timeout); + uint64_t poll_exit = m_manager.get1394Service().getCurrentTimeAsUsecs(); + if (err == -1) { + if (errno == EINTR) { + return true; + } + debugFatal("%p, poll error: %s\n", this, strerror (errno)); + return false; + } + uint64_t iter_enter=0; + uint64_t iter_exit=0; + if(m_poll_fd.revents & (POLLIN)) { + iter_enter = m_manager.get1394Service().getCurrentTimeAsUsecs(); + if(!iterate()) { + debugOutput( DEBUG_LEVEL_VERBOSE, + "IsoHandler (%p): Failed to iterate handler\n", + this); + return false; + } + iter_exit = m_manager.get1394Service().getCurrentTimeAsUsecs(); + } else { + if (m_poll_fd.revents & POLLERR) { + debugWarning("error on fd for %p\n", this); + } + if (m_poll_fd.revents & POLLHUP) { + debugWarning("hangup on fd for %p\n",this); + } + } + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "(%c %p) poll took %lldus, iterate took %lldus\n", + (getType()==eHT_Receive?'R':'X'), this, + poll_exit-poll_enter, iter_exit-iter_enter); + return true; +#else + // iterate blocks if no 1394 data is available + // so poll'ing is not really necessary + + bool result = true; + while(result && m_Client->canProcessPackets()) { + result = iterate(); + debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) Iterate returned: %d\n", + this, (m_type==eHT_Receive?"Receive":"Transmit"), result); + } + return result; +#endif + } else { + debugError("waitForClient() failed.\n"); + return false; + } +} + +bool +IsoHandler::iterate() { + debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) Iterating ISO handler\n", + this, (m_type==eHT_Receive?"Receive":"Transmit")); + if(m_State == E_Running) { +#if ISOHANDLER_FLUSH_BEFORE_ITERATE + flush(); +#endif + if(raw1394_loop_iterate(m_handle)) { + debugOutput( DEBUG_LEVEL_VERBOSE, + "IsoHandler (%p): Failed to iterate handler: %s\n", + this, strerror(errno)); + return false; + } + return true; + } else { + debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) Not iterating a non-running handler...\n", + this, (m_type==eHT_Receive?"Receive":"Transmit")); + return false; + } +} + +bool +IsoHandler::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_Thread) { + if (m_realtime) { + m_Thread->AcquireRealTime(m_priority); + } else { + m_Thread->DropRealTime(); + } + } + return true; +} + +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); + } + +#if ISOHANDLER_PER_HANDLER_THREAD + // create a thread to iterate ourselves + debugOutput( DEBUG_LEVEL_VERBOSE, "Start thread for %p...\n", this); + m_Thread = new Util::PosixThread(this, m_realtime, m_priority, + PTHREAD_CANCEL_DEFERRED); + if(!m_Thread) { + debugFatal("No thread\n"); + return false; + } + if (m_Thread->Start() != 0) { + debugFatal("Could not start update thread\n"); + return false; + } +#endif + + // 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; + } + + m_poll_fd.events = 0; + + // 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); + return 0; +} + +void IsoHandler::dumpInfo() +{ + int channel=-1; + if (m_Client) channel=m_Client->getChannel(); + + debugOutputShort( DEBUG_LEVEL_NORMAL, " Handler type................: %s\n", + (this->getType() == eHT_Receive ? "Receive" : "Transmit")); + 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); + } + debugOutputShort( DEBUG_LEVEL_NORMAL, " Packet count................: %10d (%5d dropped)\n", + this->getPacketCount(), this->getDroppedCount()); +} + +void IsoHandler::setVerboseLevel(int l) +{ + setDebugLevel(l); + if(m_Thread) m_Thread->setVerboseLevel(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) { + + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, + "received packet: length=%d, channel=%d, cycle=%d\n", + length, channel, cycle ); + m_packetcount++; + m_dropped += dropped; + + if(m_Client) { + return m_Client->putPacket(data, length, channel, tag, sy, cycle, dropped); + } + + 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) { + + debugOutput( DEBUG_LEVEL_ULTRA_VERBOSE, + "sending packet: length=%d, cycle=%d\n", + *length, cycle ); + m_packetcount++; + m_dropped += dropped; + + if(m_Client) { + return m_Client->getPacket(data, length, tag, sy, cycle, dropped, m_max_packet_size); + } + 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; + } + } + + m_poll_fd.events = POLLIN; + m_State = E_Running; + return true; +} Index: /branches/api-cleanup/src/libieee1394/csr1212.c =================================================================== --- /branches/api-cleanup/src/libieee1394/csr1212.c (revision 742) +++ /branches/api-cleanup/src/libieee1394/csr1212.c (revision 742) @@ -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 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 . + * + */ +/* + * 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/api-cleanup/src/libieee1394/IsoHandler.h =================================================================== --- /branches/api-cleanup/src/libieee1394/IsoHandler.h (revision 803) +++ /branches/api-cleanup/src/libieee1394/IsoHandler.h (revision 803) @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +#ifndef __FFADO_ISOHANDLER__ +#define __FFADO_ISOHANDLER__ + +#include "debugmodule/debugmodule.h" +#include "IsoHandlerManager.h" + +#include "libutil/Thread.h" + +enum raw1394_iso_disposition; + +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 Util::RunnableInterface +{ +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); + + 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); + +public: + // runnable interface + bool Init(); + bool Execute(); + bool iterate(); + + int getFileDescriptor() { return raw1394_get_fd(m_handle);}; + bool setThreadParameters(bool rt, int priority); + + 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;}; + + 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;}; + + int getPacketCount() {return m_packetcount;}; + void resetPacketCount() {m_packetcount=0;}; + + int getDroppedCount() {return m_dropped;}; + void resetDroppedCount() {m_dropped=0;}; + + 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 waitForClient(); + bool tryWaitForClient(); + +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_packetcount; + int m_dropped; + Streaming::StreamProcessor *m_Client; + + int handleBusReset(unsigned int generation); + + static int busreset_handler(raw1394handle_t handle, unsigned int generation); + + struct pollfd m_poll_fd; + int m_poll_timeout; + // threading + bool m_realtime; + int m_priority; + Util::Thread * m_Thread; + + enum raw1394_iso_speed m_speed; + unsigned int m_prebuffers; + + // the state machine + enum EHandlerStates { + E_Created, + E_Initialized, + E_Prepared, + E_Running, + E_Error + }; + enum EHandlerStates m_State; + DECLARE_DEBUG_MODULE; +}; + +#endif /* __FFADO_ISOHANDLER__ */ + + + Index: /branches/api-cleanup/src/ffadodevice.cpp =================================================================== --- /branches/api-cleanup/src/ffadodevice.cpp (revision 750) +++ /branches/api-cleanup/src/ffadodevice.cpp (revision 750) @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "ffadodevice.h" + +#include "libieee1394/configrom.h" +#include "libieee1394/ieee1394service.h" + +#include +#include + +#include + +IMPL_DEBUG_MODULE( FFADODevice, FFADODevice, DEBUG_LEVEL_NORMAL ); + +FFADODevice::FFADODevice( DeviceManager& d, std::auto_ptr( configRom ) ) + : Control::Container() + , m_pDeviceManager( d ) + , m_pConfigRom( configRom ) +{ + 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"); + } +} + +FFADODevice::~FFADODevice() +{ + if (!deleteElement(&getConfigRom())) { + debugWarning("failed to remove ConfigRom from Control::Container\n"); + } +} + +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) +{ + 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; +} + +void +FFADODevice::handleBusReset() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Handle bus reset...\n"); + + // update the config rom node id + sleep(1); + getConfigRom().setVerboseLevel(getDebugLevel()); + getConfigRom().updatedNodeId(); + +} + +void +FFADODevice::setVerboseLevel(int l) +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l ); + setDebugLevel(l); + getConfigRom().setVerboseLevel(l); +} + +void +FFADODevice::showDevice() +{ + 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(); +} + + +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/api-cleanup/src/devicemanager.h =================================================================== --- /branches/api-cleanup/src/devicemanager.h (revision 777) +++ /branches/api-cleanup/src/devicemanager.h (revision 777) @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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 + +#include +#include + +class Ieee1394Service; +class FFADODevice; +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< Functor* > FunctorVector; +typedef std::vector< Functor* >::iterator FunctorVectorIterator; + +class DeviceManager + : public Util::OptionContainer, + public Control::Container +{ +public: + DeviceManager(); + ~DeviceManager(); + + bool setThreadParameters(bool rt, int priority); + + bool initialize(); + bool deinitialize(); + + bool addSpecString(char *); + bool isSpecStringValid(std::string s); + + bool discover(); + bool initStreaming(); + bool prepareStreaming(); + bool finishStreaming(); + bool startStreaming(); + bool stopStreaming(); + bool resetStreaming(); + bool 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 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; + +public: // FIXME: this should be better + Streaming::StreamProcessorManager& getStreamProcessorManager() + {return *m_processorManager;}; +private: + Streaming::StreamProcessorManager* m_processorManager; +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/api-cleanup/src/libstreaming/motu/MotuPort.h =================================================================== --- /branches/api-cleanup/src/libstreaming/motu/MotuPort.h (revision 742) +++ /branches/api-cleanup/src/libstreaming/motu/MotuPort.h (revision 742) @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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(std::string name, + enum E_Direction direction, + int position, + int size) + : AudioPort(name, direction), + MotuPortInfo( position, size) // TODO: add more port information parameters here if nescessary + {}; + + virtual ~MotuAudioPort() {}; + +protected: + +}; + +/*! +\brief The Base Class for an Motu Midi Port + + +*/ +class MotuMidiPort + : public MidiPort, public MotuPortInfo +{ + +public: + + MotuMidiPort(std::string name, + enum E_Direction direction, + int position) + : MidiPort(name, direction), + MotuPortInfo(position, 0) // TODO: add more port information parameters here if nescessary + {}; + + + virtual ~MotuMidiPort() {}; + +protected: + +}; + +/*! +\brief The Base Class for an Motu Control Port + + +*/ +class MotuControlPort + : public ControlPort, public MotuPortInfo +{ + +public: + + MotuControlPort(std::string name, + enum E_Direction direction, + int position) + : ControlPort(name, direction), + MotuPortInfo(position, 2) // TODO: add more port information parameters here if nescessary + {}; + + + virtual ~MotuControlPort() {}; + +protected: + +}; + +} // end of namespace Streaming + +#endif /* __FFADO_MOTUPORT__ */ + Index: /branches/api-cleanup/src/libstreaming/motu/MotuTransmitStreamProcessor.cpp =================================================================== --- /branches/api-cleanup/src/libstreaming/motu/MotuTransmitStreamProcessor.cpp (revision 798) +++ /branches/api-cleanup/src/libstreaming/motu/MotuTransmitStreamProcessor.cpp (revision 798) @@ -0,0 +1,666 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "config.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 +#include + +// Set to 1 to enable the generation of a 1 kHz test tone in analog output 1 +#define TESTTONE 1 + +#if TESTTONE +#include +#endif + +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 ) +{} + + +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, + int cycle, unsigned int dropped, unsigned int max_length ) +{ + // 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; + + // FIXME: should become a define + // the absolute minimum number of cycles we want to transmit + // a packet ahead of the presentation time. The nominal time + // the packet is transmitted ahead of the presentation time is + // given by MOTU_TRANSMIT_TRANSFER_DELAY (in ticks), but in case we + // are too late for that, this constant defines how late we can + // be. + const int min_cycles_before_presentation = 1; + // FIXME: should become a define + // the absolute maximum number of cycles we want to transmit + // a packet ahead of the ideal transmit time. The nominal time + // the packet is transmitted ahead of the presentation time is + // given by MOTU_TRANSMIT_TRANSFER_DELAY (in ticks), but we can send + // packets early if we want to. (not completely according to spec) + const int max_cycles_to_transmit_early = 2; + + 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; + m_last_timestamp = presentation_time; + + // 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 ); + + if (dropped) { + debugOutput ( DEBUG_LEVEL_VERBOSE, + "Gen HDR: 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 ) ); + } + // 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 <= 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 >= 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, m_last_timestamp); + if (m_tx_dbc > 0xff) + m_tx_dbc -= 0x100; + return eCRV_Packet; + } + else // definitely too late + { + return eCRV_XRun; + } + } + else if(cycles_until_transmit <= max_cycles_to_transmit_early) + { + // it's time send the packet + m_tx_dbc += fillDataPacketHeader((quadlet_t *)data, length, m_last_timestamp); + 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 > 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, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int max_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().getActualRate(); + +#if TESTTONE + // FIXME: remove this hacked in 1 kHz test signal to + // analog-1 when testing is complete. + signed int i, int_tpf = (int)ticks_per_frame; + unsigned char *sample = data+8+16; + for (i=0; i= 24576000) { + a_cx -= 24576000; + } + } +#endif + + // Set up each frames's SPH. + for (int i=0; i < n_events; i++, quadlet += dbs) { +//FIXME: not sure which is best for the MOTU +// int64_t ts_frame = addTicks(ts, (unsigned int)(i * ticks_per_frame)); + int64_t ts_frame = addTicks(m_last_timestamp, (unsigned int)(i * ticks_per_frame)); + *quadlet = htonl(fullTicksToSph(ts_frame)); + } + + // Process all ports that should be handled on a per-packet base + // this is MIDI for AMDTP (due to the need of DBC, which is lost + // when putting the events in the ringbuffer) + // for motu this might also be control data, however as control + // data isn't time specific I would also include it in the period + // based processing + + // FIXME: m_tx_dbc probably needs to be initialised to a non-zero + // value somehow so MIDI sync is possible. For now we ignore + // this issue. + if (!encodePacketPorts((quadlet_t *)(data+8), n_events, m_tx_dbc)) { + debugWarning("Problem encoding Packet Ports\n"); + } + + return eCRV_OK; + } + else return eCRV_XRun; + +} + +enum StreamProcessor::eChildReturnValue +MotuTransmitStreamProcessor::generateSilentPacketHeader ( + unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int max_length ) +{ + debugOutput ( DEBUG_LEVEL_VERY_VERBOSE, "XMIT NONE: CY=%04u, TSP=%011llu (%04u)\n", + cycle, 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::generateSilentPacketData ( + unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int max_length ) +{ + return eCRV_OK; // no need to do anything +} + +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 = htonl(0x00000400 | ((m_Parent.get1394Service().getLocalNodeId()&0x3f)<<24) | m_tx_dbc | (dbs<<16)); + quadlet++; + *quadlet = htonl(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 = htonl(0x00000400 | ((m_Parent.get1394Service().getLocalNodeId()&0x3f)<<24) | m_tx_dbc | (dbs<<16)); + quadlet++; + *quadlet = htonl(0x8222ffff); + quadlet++; + *length = 8; + return 0; +} + +bool MotuTransmitStreamProcessor::prepareChild() +{ + debugOutput ( DEBUG_LEVEL_VERBOSE, "Preparing (%p)...\n", this ); + + +#if 0 + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + if ( ( *it )->getPortType() == Port::E_Midi ) + { + // we use a timing unit of 10ns + // this makes sure that for the max syt interval + // we don't have rounding, and keeps the numbers low + // we have 1 slot every 8 events + // we have syt_interval events per packet + // => syt_interval/8 slots per packet + // packet rate is 8000pkt/sec => interval=125us + // so the slot interval is (1/8000)/(syt_interval/8) + // or: 1/(1000 * syt_interval) sec + // which is 1e9/(1000*syt_interval) nsec + // or 100000/syt_interval 'units' + // the event interval is fixed to 320us = 32000 'units' + if ( ! ( *it )->useRateControl ( true, ( 100000/m_syt_interval ),32000, false ) ) + { + debugFatal ( "Could not set signal type to PeriodSignalling" ); + return false; + } + break; + } + } +#endif + 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; + + // FIXME: ensure the MIDI and control streams are all zeroed until + // such time as they are fully implemented. + for (i=0; iisDisabled()) {continue;}; + + //FIXME: make this into a static_cast when not DEBUG? + Port *port=dynamic_cast(*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 MBLA events",(*it)->getName().c_str()); + no_problem=false; + } + break; + // midi is a packet based port, don't process + // case MotuPortInfo::E_Midi: + // 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_PeriodPorts.begin(); + it != m_PeriodPorts.end(); + ++it ) { + //FIXME: make this into a static_cast when not DEBUG? + Port *port=dynamic_cast(*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; + // midi is a packet based port, don't process + // case MotuPortInfo::E_Midi: + // break; + + default: // ignore + break; + } + } + return no_problem; +} + +/** + * @brief encode a packet for the packet-based ports + * + * @param data Packet data + * @param nevents number of events in data (including events of other ports & port types) + * @param dbc DataBlockCount value for this packet + * @return true if all successfull + */ +bool MotuTransmitStreamProcessor::encodePacketPorts(quadlet_t *data, unsigned int nevents, + unsigned int dbc) { + bool ok=true; + char byte; + + // 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 for MIDI data going + // directly to the MOTU isn't structured in quadlets anyway; it is a + // sequence of 3 unaligned bytes. + unsigned char *target = NULL; + + for ( PortVectorIterator it = m_PacketPorts.begin(); + it != m_PacketPorts.end(); + ++it ) { + + Port *port=static_cast(*it); + assert(port); // this should not fail!! + + // Currently the only packet type of events for MOTU + // is MIDI in mbla. However in future control data + // might also be sent via "packet" events. + // assert(pinfo->getFormat()==MotuPortInfo::E_Midi); + + // FIXME: MIDI output is completely untested at present. + switch (port->getPortType()) { + case Port::E_Midi: { + MotuMidiPort *mp=static_cast(*it); + + // Send a byte if we can. MOTU MIDI data is + // sent using a 3-byte sequence starting at + // the port's position. For now we'll + // always send in the first event of a + // packet, but this might need refinement + // later. + if (mp->canRead()) { + mp->readEvent(&byte); + target = (unsigned char *)data + mp->getPosition(); + *(target++) = 0x01; + *(target++) = 0x00; + *(target++) = byte; + } + break; + } + default: + debugOutput(DEBUG_LEVEL_VERBOSE, "Unknown packet-type port type %d\n",port->getPortType()); + return ok; + } + } + + return ok; +} + +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(p->getDataType()) { + default: + case Port::E_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 Port::E_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 + unsigned int v = (int)(*buffer * 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 (p->getDataType()) { + default: + case Port::E_Int24: + case Port::E_Float: + for (j = 0; j < nevents; j++) { + *target = *(target+1) = *(target+2) = 0; + target += m_event_size; + } + break; + } + + return 0; +} + +} // end of namespace Streaming Index: /branches/api-cleanup/src/libstreaming/motu/MotuReceiveStreamProcessor.cpp =================================================================== --- /branches/api-cleanup/src/libstreaming/motu/MotuReceiveStreamProcessor.cpp (revision 750) +++ /branches/api-cleanup/src/libstreaming/motu/MotuReceiveStreamProcessor.cpp (revision 750) @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "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 +#include + +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 (CYCLE_TIMER_GET_CYCLES(sph) > now_cycles + 1000) { + if (ts_sec) + ts_sec--; + else + ts_sec = 127; + } else + if (now_cycles > CYCLE_TIMER_GET_CYCLES(sph) + 1000) { + if (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 ) +{} + +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 + * @param dropped + * @return + */ +enum StreamProcessor::eChildReturnValue +MotuReceiveStreamProcessor::processPacketHeader(unsigned char *data, unsigned int length, + unsigned char channel, unsigned char tag, unsigned char sy, + unsigned int cycle, unsigned int dropped) +{ + 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(ntohl(quadlet[0]), 23, 8); // Size of one event in terms of fdf_size + unsigned int fdf_size = get_bits(ntohl(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 = ntohl(*(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 cycle + * @param dropped + * @return + */ +enum StreamProcessor::eChildReturnValue +MotuReceiveStreamProcessor::processPacketData(unsigned char *data, unsigned int length, + unsigned char channel, unsigned char tag, unsigned char sy, + unsigned int cycle, unsigned int dropped_cycles) { + quadlet_t* quadlet = (quadlet_t*) data; + + unsigned int dbs = get_bits(ntohl(quadlet[0]), 23, 8); // Size of one event in terms of fdf_size + unsigned int fdf_size = get_bits(ntohl(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)) { + int dbc = get_bits(ntohl(quadlet[0]), 8, 8); + // process all ports that should be handled on a per-packet base + // this is MIDI for AMDTP (due to the need of DBC) + if(isRunning()) { + if (!decodePacketPorts((quadlet_t *)(data+8), n_events, dbc)) { + debugWarning("Problem decoding Packet Ports\n"); + } + } + 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; + for ( PortVectorIterator it = m_PeriodPorts.begin(); + it != m_PeriodPorts.end(); + ++it ) { + if((*it)->isDisabled()) {continue;}; + + //FIXME: make this into a static_cast when not DEBUG? + Port *port=dynamic_cast(*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; + // midi is a packet based port, don't process + // case MotuPortInfo::E_Midi: + // break; + + default: // ignore + break; + } + } + return no_problem; +} + +/** + * @brief decode a packet for the packet-based ports + * + * @param data Packet data + * @param nevents number of events in data (including events of other ports & port types) + * @param dbc DataBlockCount value for this packet + * @return true if all successfull + */ +bool MotuReceiveStreamProcessor::decodePacketPorts(quadlet_t *data, unsigned int nevents, + unsigned int dbc) { + bool ok=true; + + // Use char here since the source address won't necessarily be + // aligned; use of an unaligned quadlet_t may cause issues on + // certain architectures. Besides, the source for MIDI data going + // directly to the MOTU isn't structured in quadlets anyway; it is a + // sequence of 3 unaligned bytes. + unsigned char *src = NULL; + + for ( PortVectorIterator it = m_PacketPorts.begin(); + it != m_PacketPorts.end(); + ++it ) { + + Port *port=dynamic_cast(*it); + assert(port); // this should not fail!! + + // Currently the only packet type of events for MOTU + // is MIDI in mbla. However in future control data + // might also be sent via "packet" events, so allow + // for this possible expansion. + + // FIXME: MIDI input is completely untested at present. + switch (port->getPortType()) { + case Port::E_Midi: { + MotuMidiPort *mp=static_cast(*it); + signed int sample; + unsigned int j = 0; + // Get MIDI bytes if present anywhere in the + // packet. MOTU MIDI data is sent using a + // 3-byte sequence starting at the port's + // position. It's thought that there can never + // be more than one MIDI byte per packet, but + // for completeness we'll check the entire packet + // anyway. + src = (unsigned char *)data + mp->getPosition(); + while (j < nevents) { + if (*src==0x01 && *(src+1)==0x00) { + sample = *(src+2); + if (!mp->writeEvent(&sample)) { + debugWarning("MIDI packet port events lost\n"); + ok = false; + } + } + j++; + src += m_event_size; + } + break; + } + default: + debugOutput(DEBUG_LEVEL_VERBOSE, "Unknown packet-type port format %d\n",port->getPortType()); + return ok; + } + } + + return ok; +} + +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(p->getDataType()) { + default: + case Port::E_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 Port::E_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 + + unsigned int v = (*src_data<<16)+(*(src_data+1)<<8)+*(src_data+2); + + // sign-extend highest bit of 24-bit int + int tmp = (int)(v << 8) / 256; + + *buffer = tmp * multiplier; + + buffer++; + src_data+=m_event_size; + } + } + break; + } + + return 0; +} + +} // end of namespace Streaming Index: /branches/api-cleanup/src/libstreaming/motu/MotuPortInfo.cpp =================================================================== --- /branches/api-cleanup/src/libstreaming/motu/MotuPortInfo.cpp (revision 742) +++ /branches/api-cleanup/src/libstreaming/motu/MotuPortInfo.cpp (revision 742) @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "MotuPortInfo.h" + +namespace Streaming { + + +} // end of namespace Streaming Index: /branches/api-cleanup/src/libstreaming/motu/MotuTransmitStreamProcessor.h =================================================================== --- /branches/api-cleanup/src/libstreaming/motu/MotuTransmitStreamProcessor.h (revision 748) +++ /branches/api-cleanup/src/libstreaming/motu/MotuTransmitStreamProcessor.h (revision 748) @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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; + +/*! +\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, + int cycle, unsigned int dropped, unsigned int max_length); + enum eChildReturnValue generatePacketData(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int max_length); + enum eChildReturnValue generateSilentPacketHeader(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int max_length); + enum eChildReturnValue generateSilentPacketData(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int max_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); + + /* + * 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; + +}; + +} // end of namespace Streaming + +#endif /* __FFADO_MOTUTRANSMITSTREAMPROCESSOR__ */ + Index: /branches/api-cleanup/src/libstreaming/motu/MotuReceiveStreamProcessor.h =================================================================== --- /branches/api-cleanup/src/libstreaming/motu/MotuReceiveStreamProcessor.h (revision 748) +++ /branches/api-cleanup/src/libstreaming/motu/MotuReceiveStreamProcessor.h (revision 748) @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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 { + +class MotuAudioPort; +/*! + * \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 channel, unsigned char tag, unsigned char sy, + unsigned int cycle, unsigned int dropped); + enum eChildReturnValue processPacketData(unsigned char *data, unsigned int length, + unsigned char channel, unsigned char tag, unsigned char sy, + unsigned int cycle, unsigned int dropped); + + 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); + + /* + * 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; +}; + + +} // end of namespace Streaming + +#endif /* __FFADO_MOTURECEIVESTREAMPROCESSOR__ */ + Index: /branches/api-cleanup/src/libstreaming/motu/MotuPort.cpp =================================================================== --- /branches/api-cleanup/src/libstreaming/motu/MotuPort.cpp (revision 742) +++ /branches/api-cleanup/src/libstreaming/motu/MotuPort.cpp (revision 742) @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "MotuPort.h" +#include + +namespace Streaming { + +} // end of namespace Streaming Index: /branches/api-cleanup/src/libstreaming/motu/MotuPortInfo.h =================================================================== --- /branches/api-cleanup/src/libstreaming/motu/MotuPortInfo.h (revision 742) +++ /branches/api-cleanup/src/libstreaming/motu/MotuPortInfo.h (revision 742) @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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/api-cleanup/src/libstreaming/amdtp/AmdtpPort.h =================================================================== --- /branches/api-cleanup/src/libstreaming/amdtp/AmdtpPort.h (revision 742) +++ /branches/api-cleanup/src/libstreaming/amdtp/AmdtpPort.h (revision 742) @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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(std::string name, + enum E_Direction direction, + int position, + int location, + enum E_Formats format) + : AudioPort(name, direction), + AmdtpPortInfo(position, location, format) + {}; + + virtual ~AmdtpAudioPort() {}; + +protected: + +}; + +/*! +\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(std::string name, + enum E_Direction direction, + int position, + int location, + enum E_Formats format) + : MidiPort(name, direction), + AmdtpPortInfo(position, location, format) + {}; + + + virtual ~AmdtpMidiPort() {}; + +protected: + +}; + +} // end of namespace Streaming + +#endif /* __FFADO_AMDTPPORT__ */ Index: /branches/api-cleanup/src/libstreaming/amdtp/AmdtpTransmitStreamProcessor.cpp =================================================================== --- /branches/api-cleanup/src/libstreaming/amdtp/AmdtpTransmitStreamProcessor.cpp (revision 798) +++ /branches/api-cleanup/src/libstreaming/amdtp/AmdtpTransmitStreamProcessor.cpp (revision 798) @@ -0,0 +1,760 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "config.h" +#include "AmdtpTransmitStreamProcessor.h" +#include "AmdtpPort.h" +#include "../StreamProcessorManager.h" +#include "devicemanager.h" + +#include "libutil/Time.h" + +#include "libieee1394/ieee1394service.h" +#include "libieee1394/IsoHandlerManager.h" +#include "libieee1394/cycletimer.h" + +#include +#include + +namespace Streaming +{ + +/* transmit */ +AmdtpTransmitStreamProcessor::AmdtpTransmitStreamProcessor(FFADODevice &parent, int dimension) + : StreamProcessor(parent, ePT_Transmit) + , m_dimension( dimension ) + , m_dbc( 0 ) +{} + +enum StreamProcessor::eChildReturnValue +AmdtpTransmitStreamProcessor::generatePacketHeader ( + unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int max_length ) +{ + 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_1394service.getLocalNodeId() & 0x3f; + + 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; + + 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; + m_last_timestamp = presentation_time; + + // 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 ); + + // 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 ); + + if (dropped) { + debugOutput ( DEBUG_LEVEL_VERBOSE, + "Gen HDR: 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 ) ); + } + // 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_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 + { + unsigned int now_cycle = ( unsigned int ) ( TICKS_TO_CYCLES ( m_1394service.getCycleTimerTicks() ) ); + + debugOutput ( DEBUG_LEVEL_VERBOSE, + "Insufficient frames (NP): N=%02d, CY=%04u, TC=%04u, CUT=%04d, NOW=%04d\n", + fc, cycle, transmit_at_cycle, cycles_until_transmit, now_cycle ); + debugWarning("Insufficient frames (NP): N=%02d, CY=%04u, TC=%04u, CUT=%04d, NOW=%04d\n", + fc, cycle, transmit_at_cycle, cycles_until_transmit, now_cycle ); + + // 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_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) ); + //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, m_last_timestamp); +// 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, m_last_timestamp); + return (fc < (signed)(2*m_syt_interval) ? eCRV_Defer : 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 > AMDTP_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 +AmdtpTransmitStreamProcessor::generatePacketData ( + unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int max_length ) +{ + struct iec61883_packet *packet = ( struct iec61883_packet * ) data; + if ( m_data_buffer->readFrames ( m_syt_interval, ( char * ) ( data + 8 ) ) ) + { + // process all ports that should be handled on a per-packet base + // this is MIDI for AMDTP (due to the need of DBC) + if ( !encodePacketPorts ( ( quadlet_t * ) ( data+8 ), m_syt_interval, packet->dbc ) ) + { + debugWarning ( "Problem encoding Packet Ports\n" ); + } + debugOutput ( DEBUG_LEVEL_ULTRA_VERBOSE, "XMIT DATA (cy %04d): TSP=%011llu (%04u)\n", + cycle, 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, + int cycle, unsigned int dropped, unsigned int max_length ) +{ + struct iec61883_packet *packet = ( struct iec61883_packet * ) data; + debugOutput ( DEBUG_LEVEL_ULTRA_VERBOSE, "XMIT NONE (cy %04d): CY=%04u, TSP=%011llu (%04u)\n", + cycle, m_last_timestamp, ( unsigned int ) TICKS_TO_CYCLES ( m_last_timestamp ) ); + + /* Our node ID can change after a bus reset, so it is best to fetch + * our node ID for each packet. */ + packet->sid = m_1394service.getLocalNodeId() & 0x3f; + + 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::generateSilentPacketData ( + unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int max_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 = ntohs ( timestamp_SYT ); + + *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 have the usual amount of events as dummy data (?) + packet->fdf = IEC61883_FDF_NODATA; + packet->syt = 0xffff; + + // FIXME: either make this a setting or choose + bool send_payload=true; + if ( send_payload ) + { + // this means no-data packets with payload (DICE doesn't like that) + *length = 2*sizeof ( quadlet_t ) + m_syt_interval * m_dimension * sizeof ( quadlet_t ); + return m_syt_interval; + } + else + { + // dbc is not incremented + // this means no-data packets without payload + *length = 2*sizeof ( quadlet_t ); + return 0; + } +} + +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 ); + + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + if ( ( *it )->getPortType() == Port::E_Midi ) + { + // we use a timing unit of 10ns + // this makes sure that for the max syt interval + // we don't have rounding, and keeps the numbers low + // we have 1 slot every 8 events + // we have syt_interval events per packet + // => syt_interval/8 slots per packet + // packet rate is 8000pkt/sec => interval=125us + // so the slot interval is (1/8000)/(syt_interval/8) + // or: 1/(1000 * syt_interval) sec + // which is 1e9/(1000*syt_interval) nsec + // or 100000/syt_interval 'units' + // the event interval is fixed to 320us = 32000 'units' + if ( ! ( *it )->useRateControl ( true, ( 100000/m_syt_interval ),32000, false ) ) + { + debugFatal ( "Could not set signal type to PeriodSignalling" ); + return false; + } + break; + } + } + return true; +} + +/* +* compose the event streams for the packets from the port buffers +*/ +bool AmdtpTransmitStreamProcessor::processWriteBlock ( char *data, + unsigned int nevents, unsigned int offset ) +{ + bool no_problem = true; + + for ( PortVectorIterator it = m_PeriodPorts.begin(); + it != m_PeriodPorts.end(); + ++it ) + { + if ( (*it)->isDisabled() ) { continue; }; + + //FIXME: make this into a static_cast when not DEBUG? + AmdtpPortInfo *pinfo = dynamic_cast ( *it ); + assert ( pinfo ); // this should not fail!! + + switch( pinfo->getFormat() ) + { + case AmdtpPortInfo::E_MBLA: + if( encodePortToMBLAEvents(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 AmdtpPortInfo::E_SPDIF: // still unimplemented + break; + default: // ignore + break; + } + } + return no_problem; +} + +bool +AmdtpTransmitStreamProcessor::transmitSilenceBlock( + char *data, unsigned int nevents, unsigned int offset) +{ + bool no_problem = true; + for(PortVectorIterator it = m_PeriodPorts.begin(); + it != m_PeriodPorts.end(); + ++it ) + { + //FIXME: make this into a static_cast when not DEBUG? + AmdtpPortInfo *pinfo=dynamic_cast(*it); + assert(pinfo); // this should not fail!! + + switch( pinfo->getFormat() ) + { + case AmdtpPortInfo::E_MBLA: + if ( encodeSilencePortToMBLAEvents(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 AmdtpPortInfo::E_SPDIF: // still unimplemented + break; + default: // ignore + break; + } + } + return no_problem; +} + +/** +* @brief decode a packet for the packet-based ports +* +* @param data Packet data +* @param nevents number of events in data (including events of other ports & port types) +* @param dbc DataBlockCount value for this packet +* @return true if all successfull +*/ +bool AmdtpTransmitStreamProcessor::encodePacketPorts ( quadlet_t *data, unsigned int nevents, unsigned int dbc ) +{ + bool ok=true; + quadlet_t byte; + + quadlet_t *target_event=NULL; + unsigned int j; + + for ( PortVectorIterator it = m_PacketPorts.begin(); + it != m_PacketPorts.end(); + ++it ) + { + +#ifdef DEBUG + AmdtpPortInfo *pinfo=dynamic_cast ( *it ); + assert ( pinfo ); // this should not fail!! + + // the only packet type of events for AMDTP is MIDI in mbla + assert ( pinfo->getFormat() ==AmdtpPortInfo::E_Midi ); +#endif + + AmdtpMidiPort *mp=static_cast ( *it ); + + // we encode this directly (no function call) due to the high frequency + /* idea: + spec says: current_midi_port=(dbc+j)%8; + => if we start at (dbc+stream->location-1)%8, + we'll start at the right event for the midi port. + => if we increment j with 8, we stay at the right event. + */ + // FIXME: as we know in advance how big a packet is (syt_interval) we can + // predict how much loops will be present here + // first prefill the buffer with NO_DATA's on all time muxed channels + + for ( j = ( dbc & 0x07 ) +mp->getLocation(); j < nevents; j += 8 ) + { + + quadlet_t tmpval; + + target_event= ( quadlet_t * ) ( data + ( ( j * m_dimension ) + mp->getPosition() ) ); + + if ( mp->canRead() ) // we can send a byte + { + mp->readEvent ( &byte ); + byte &= 0xFF; + tmpval=htonl ( + IEC61883_AM824_SET_LABEL ( ( byte ) <<16, + IEC61883_AM824_LABEL_MIDI_1X ) ); + + debugOutput ( DEBUG_LEVEL_ULTRA_VERBOSE, "MIDI port %s, pos=%d, loc=%d, dbc=%d, nevents=%d, dim=%d\n", + mp->getName().c_str(), mp->getPosition(), mp->getLocation(), dbc, nevents, m_dimension ); + debugOutput ( DEBUG_LEVEL_ULTRA_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 + tmpval=htonl ( + IEC61883_AM824_SET_LABEL ( 0,IEC61883_AM824_LABEL_MIDI_NO_DATA ) ); + } + + *target_event=tmpval; + } + + } + return ok; +} + +#if USE_SSE +typedef float v4sf __attribute__ ((vector_size (16))); +typedef int v4si __attribute__ ((vector_size (16))); +typedef int v2si __attribute__ ((vector_size (8))); + +int AmdtpTransmitStreamProcessor::encodePortToMBLAEvents ( AmdtpAudioPort *p, quadlet_t *data, + unsigned int offset, unsigned int nevents ) +{ + static const float sse_multiplier[4] __attribute__((aligned(16))) = { + (float)(0x7FFFFF00), + (float)(0x7FFFFF00), + (float)(0x7FFFFF00), + (float)(0x7FFFFF00) + }; + + static const int sse_mask[4] __attribute__((aligned(16))) = { + 0x40000000, 0x40000000, 0x40000000, 0x40000000 + }; + + unsigned int out[4]; + + unsigned int j=0; + unsigned int read=0; + + quadlet_t *target_event; + + target_event= ( quadlet_t * ) ( data + p->getPosition() ); + + switch ( p->getDataType() ) + { + default: + case Port::E_Int24: + { + quadlet_t *buffer= ( quadlet_t * ) ( p->getBufferAddress() ); + + assert ( nevents + offset <= p->getBufferSize() ); + + buffer+=offset; + + for ( j = 0; j < nevents; j += 1 ) // decode max nsamples + { + *target_event = htonl ( ( * ( buffer ) & 0x00FFFFFF ) | 0x40000000 ); + buffer++; + target_event += m_dimension; + } + } + break; + case Port::E_Float: + { + const float multiplier = ( float ) ( 0x7FFFFF00 ); + float *buffer= ( float * ) ( p->getBufferAddress() ); + + assert ( nevents + offset <= p->getBufferSize() ); + + buffer+=offset; + + j=0; + if(read>3) { + for (j = 0; j < read-3; j += 4) { + asm("movups %[floatbuff], %%xmm0\n\t" + "mulps %[ssemult], %%xmm0\n\t" + "cvttps2pi %%xmm0, %[out1]\n\t" + "movhlps %%xmm0, %%xmm0\n\t" + "psrld $8, %[out1]\n\t" + "cvttps2pi %%xmm0, %[out2]\n\t" + "por %[mmxmask], %[out1]\n\t" + "psrld $8, %[out2]\n\t" + "por %[mmxmask], %[out2]\n\t" + : [out1] "=&y" (*(v2si*)&out[0]), + [out2] "=&y" (*(v2si*)&out[2]) + : [floatbuff] "m" (*(v4sf*)buffer), + [ssemult] "x" (*(v4sf*)sse_multiplier), + [mmxmask] "y" (*(v2si*)sse_mask) + : "xmm0"); + buffer += 4; + *target_event = htonl(out[0]); + target_event += m_dimension; + *target_event = htonl(out[1]); + target_event += m_dimension; + *target_event = htonl(out[2]); + target_event += m_dimension; + *target_event = htonl(out[3]); + target_event += m_dimension; + } + } + for(; j < read; ++j) { + // don't care for overflow + float v = *buffer * multiplier; // v: -231 .. 231 + unsigned int tmp = (int)v; + *target_event = htonl((tmp >> 8) | 0x40000000); + + buffer++; + target_event += m_dimension; + } + + asm volatile("emms"); + break; + } + break; + } + + return 0; +} + +#else + +int AmdtpTransmitStreamProcessor::encodePortToMBLAEvents ( AmdtpAudioPort *p, quadlet_t *data, + unsigned int offset, unsigned int nevents ) +{ + unsigned int j=0; + + quadlet_t *target_event; + + target_event= ( quadlet_t * ) ( data + p->getPosition() ); + + switch ( p->getDataType() ) + { + default: + case Port::E_Int24: + { + quadlet_t *buffer= ( quadlet_t * ) ( p->getBufferAddress() ); + + assert ( nevents + offset <= p->getBufferSize() ); + + buffer+=offset; + + for ( j = 0; j < nevents; j += 1 ) // decode max nsamples + { + *target_event = htonl ( ( * ( buffer ) & 0x00FFFFFF ) | 0x40000000 ); + buffer++; + target_event += m_dimension; + } + } + break; + case Port::E_Float: + { + const float multiplier = ( float ) ( 0x7FFFFF00 ); + float *buffer= ( float * ) ( p->getBufferAddress() ); + + assert ( nevents + offset <= p->getBufferSize() ); + + buffer+=offset; + + for ( j = 0; j < nevents; j += 1 ) // decode max nsamples + { + + // don't care for overflow + float v = *buffer * multiplier; // v: -231 .. 231 + unsigned int tmp = ( ( int ) v ); + *target_event = htonl ( ( tmp >> 8 ) | 0x40000000 ); + + buffer++; + target_event += m_dimension; + } + } + break; + } + + return 0; +} +#endif + +int AmdtpTransmitStreamProcessor::encodeSilencePortToMBLAEvents ( AmdtpAudioPort *p, quadlet_t *data, + unsigned int offset, unsigned int nevents ) +{ + unsigned int j=0; + + quadlet_t *target_event; + + target_event= ( quadlet_t * ) ( data + p->getPosition() ); + + switch ( p->getDataType() ) + { + default: + case Port::E_Int24: + case Port::E_Float: + { + for ( j = 0; j < nevents; j += 1 ) // decode max nsamples + { + *target_event = htonl ( 0x40000000 ); + target_event += m_dimension; + } + } + break; + } + + return 0; +} + +} // end of namespace Streaming Index: /branches/api-cleanup/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.cpp =================================================================== --- /branches/api-cleanup/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.cpp (revision 790) +++ /branches/api-cleanup/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.cpp (revision 790) @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#include "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 +#include + +namespace Streaming { + +/* --------------------- RECEIVE ----------------------- */ + +AmdtpReceiveStreamProcessor::AmdtpReceiveStreamProcessor(FFADODevice &parent, int dimension) + : StreamProcessor(parent, ePT_Receive) + , m_dimension( dimension ) +{} + +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(); + return true; +} + + +/** + * Processes packet header to extract timestamps and so on + * @param data + * @param length + * @param channel + * @param tag + * @param sy + * @param cycle + * @param dropped + * @return + */ +enum StreamProcessor::eChildReturnValue +AmdtpReceiveStreamProcessor::processPacketHeader(unsigned char *data, unsigned int length, + unsigned char channel, unsigned char tag, unsigned char sy, + unsigned int cycle, unsigned int dropped) +{ + #ifdef DEBUG + static uint32_t now_prev=0; + static uint64_t now_prev_ticks=0; + #endif + + 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) { + uint32_t now = m_1394service.getCycleTimer(); + + #ifdef DEBUG + uint64_t now_ticks = CYCLE_TIMER_TO_TICKS(now); + + if (diffTicks(now_ticks, now_prev_ticks) < 0) { + debugWarning("non-monotonic CTR on cycle %04u: %llu -> %llu\n", cycle, now_prev_ticks, now_ticks); + debugWarning(" : %08X -> %08X\n", now_prev, now); + debugOutput ( DEBUG_LEVEL_VERBOSE, + " current: %011llu (%03us %04ucy %04uticks)\n", + now, + (unsigned int)TICKS_TO_SECS( now ), + (unsigned int)TICKS_TO_CYCLES( now ), + (unsigned int)TICKS_TO_OFFSET( now ) ); + debugOutput ( DEBUG_LEVEL_VERBOSE, + " prev : %011llu (%03us %04ucy %04uticks)\n", + now_prev, + (unsigned int)TICKS_TO_SECS( now_prev ), + (unsigned int)TICKS_TO_CYCLES( now_prev ), + (unsigned int)TICKS_TO_OFFSET( now_prev ) ); + } + now_prev = now; + now_prev_ticks=now_ticks; + #endif + + //=> convert the SYT to a full timestamp in ticks + m_last_timestamp = sytRecvToFullTicks((uint32_t)ntohs(packet->syt), + cycle, now); + } + 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 cycle + * @param dropped + * @return + */ +enum StreamProcessor::eChildReturnValue +AmdtpReceiveStreamProcessor::processPacketData(unsigned char *data, unsigned int length, + unsigned char channel, unsigned char tag, unsigned char sy, + unsigned int cycle, unsigned int dropped_cycles) { + 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()) { + debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"STMP: %lluticks | syt_interval=%d, tpf=%f\n", + m_last_timestamp, m_syt_interval, getTicksPerFrame()); + } + #endif + + if(m_data_buffer->writeFrames(nevents, (char *)(data+8), m_last_timestamp)) { + // process all ports that should be handled on a per-packet base + // this is MIDI for AMDTP (due to the need of DBC) + if(isRunning()) { + if (!decodePacketPorts((quadlet_t *)(data+8), nevents, packet->dbc)) { + debugWarning("Problem decoding Packet Ports\n"); + } + } + 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) +{ + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "(%p)->processReadBlock(%u, %u)\n",this,nevents,offset); + + bool no_problem=true; + + for ( PortVectorIterator it = m_PeriodPorts.begin(); + it != m_PeriodPorts.end(); + ++it ) + { + if((*it)->isDisabled()) {continue;}; + + //FIXME: make this into a static_cast when not DEBUG? + + AmdtpPortInfo *pinfo=dynamic_cast(*it); + assert(pinfo); // this should not fail!! + + switch(pinfo->getFormat()) { + case AmdtpPortInfo::E_MBLA: + if(decodeMBLAEventsToPort(static_cast(*it), (quadlet_t *)data, offset, nevents)) { + debugWarning("Could not decode packet MBLA to port %s",(*it)->getName().c_str()); + no_problem=false; + } + break; + case AmdtpPortInfo::E_SPDIF: // still unimplemented + break; + /* for this processor, midi is a packet based port + case AmdtpPortInfo::E_Midi: + break;*/ + default: // ignore + break; + } + } + return no_problem; +} + +/** + * @brief decode a packet for the packet-based ports + * + * @param data Packet data + * @param nevents number of events in data (including events of other ports & port types) + * @param dbc DataBlockCount value for this packet + * @return true if all successfull + */ +bool AmdtpReceiveStreamProcessor::decodePacketPorts(quadlet_t *data, unsigned int nevents, unsigned int dbc) +{ + bool ok=true; + + quadlet_t *target_event=NULL; + unsigned int j; + + for ( PortVectorIterator it = m_PacketPorts.begin(); + it != m_PacketPorts.end(); + ++it ) + { + +#ifdef DEBUG + AmdtpPortInfo *pinfo=dynamic_cast(*it); + assert(pinfo); // this should not fail!! + + // the only packet type of events for AMDTP is MIDI in mbla + assert(pinfo->getFormat()==AmdtpPortInfo::E_Midi); +#endif + AmdtpMidiPort *mp=static_cast(*it); + + // we decode this directly (no function call) due to the high frequency + /* idea: + spec says: current_midi_port=(dbc+j)%8; + => if we start at (dbc+stream->location-1)%8, + we'll start at the right event for the midi port. + => if we increment j with 8, we stay at the right event. + */ + // FIXME: as we know in advance how big a packet is (syt_interval) we can + // predict how much loops will be present here + for(j = (dbc & 0x07)+mp->getLocation(); j < nevents; j += 8) { + target_event=(quadlet_t *)(data + ((j * m_dimension) + mp->getPosition())); + quadlet_t sample_int=ntohl(*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; + if(!mp->writeEvent(&sample_int)) { + debugWarning("Packet port events lost\n"); + ok=false; + } + } + } + + } + + return ok; +} + +#if USE_SSE +typedef float v4sf __attribute__ ((vector_size (16))); +typedef int v4si __attribute__ ((vector_size (16))); +typedef int v2si __attribute__ ((vector_size (8))); + +int +AmdtpReceiveStreamProcessor::decodeMBLAEventsToPort( + AmdtpAudioPort *p, quadlet_t *data, + unsigned int offset, unsigned int nevents) +{ + unsigned int j=0; + quadlet_t *target_event; + + target_event=(quadlet_t *)(data + p->getPosition()); + + static const float multiplier = 1.0f / (float)(0x7FFFFF); + static const float sse_multiplier[4] __attribute__((aligned(16))) = { + 1.0f / (float)(0x7FFFFF), + 1.0f / (float)(0x7FFFFF), + 1.0f / (float)(0x7FFFFF), + 1.0f / (float)(0x7FFFFF) + }; + unsigned int tmp[4]; + + switch(p->getDataType()) { + default: + case Port::E_Int24: + { + quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); + + assert(nevents + offset <= p->getBufferSize()); + + buffer+=offset; + + for(j = 0; j < nevents; j += 1) { // decode max nsamples + *(buffer)=(ntohl((*target_event) ) & 0x00FFFFFF); + buffer++; + target_event+=m_dimension; + } + } + break; + case Port::E_Float: + { + float *buffer=(float *)(p->getBufferAddress()); + + assert(nevents + offset <= p->getBufferSize()); + + buffer += offset; + j = 0; + if(nevents > 3) { + for(j = 0; j < nevents-3; j += 4) { + tmp[0] = ntohl(*target_event); + target_event += m_dimension; + tmp[1] = ntohl(*target_event); + target_event += m_dimension; + tmp[2] = ntohl(*target_event); + target_event += m_dimension; + tmp[3] = ntohl(*target_event); + target_event += m_dimension; + asm("pslld $8, %[in2]\n\t" // sign extend 24th bit + "pslld $8, %[in1]\n\t" + "psrad $8, %[in2]\n\t" + "psrad $8, %[in1]\n\t" + "cvtpi2ps %[in2], %%xmm0\n\t" + "movlhps %%xmm0, %%xmm0\n\t" + "cvtpi2ps %[in1], %%xmm0\n\t" + "mulps %[ssemult], %%xmm0\n\t" + "movups %%xmm0, %[floatbuff]" + : [floatbuff] "=m" (*(v4sf*)buffer) + : [in1] "y" (*(v2si*)tmp), + [in2] "y" (*(v2si*)(tmp+2)), + [ssemult] "x" (*(v4sf*)sse_multiplier) + : "xmm0"); + buffer += 4; + } + } + for(; j < nevents; ++j) { // decode max nsamples + unsigned int v = ntohl(*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; + } + asm volatile("emms"); + break; + } + break; + } + + return 0; +} + +#else + +int +AmdtpReceiveStreamProcessor::decodeMBLAEventsToPort( + AmdtpAudioPort *p, quadlet_t *data, + unsigned int offset, unsigned int nevents) +{ + unsigned int j=0; + quadlet_t *target_event; + + target_event=(quadlet_t *)(data + p->getPosition()); + + switch(p->getDataType()) { + default: + case Port::E_Int24: + { + quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); + + assert(nevents + offset <= p->getBufferSize()); + + buffer+=offset; + + for(j = 0; j < nevents; j += 1) { // decode max nsamples + *(buffer)=(ntohl((*target_event) ) & 0x00FFFFFF); + buffer++; + target_event+=m_dimension; + } + } + break; + case Port::E_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 + + unsigned int v = ntohl(*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; + } + } + break; + } + + return 0; +} +#endif +} // end of namespace Streaming Index: /branches/api-cleanup/src/libstreaming/amdtp/AmdtpPortInfo.cpp =================================================================== --- /branches/api-cleanup/src/libstreaming/amdtp/AmdtpPortInfo.cpp (revision 742) +++ /branches/api-cleanup/src/libstreaming/amdtp/AmdtpPortInfo.cpp (revision 742) @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "AmdtpPortInfo.h" +#include + + +namespace Streaming { + + +} // end of namespace Streaming Index: /branches/api-cleanup/src/libstreaming/amdtp/AmdtpTransmitStreamProcessor.h =================================================================== --- /branches/api-cleanup/src/libstreaming/amdtp/AmdtpTransmitStreamProcessor.h (revision 750) +++ /branches/api-cleanup/src/libstreaming/amdtp/AmdtpTransmitStreamProcessor.h (revision 750) @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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, + int cycle, unsigned int dropped, unsigned int max_length); + enum eChildReturnValue generatePacketData(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int max_length); + enum eChildReturnValue generateSilentPacketHeader(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int max_length); + enum eChildReturnValue generateSilentPacketData(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int max_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 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); + + bool encodePacketPorts(quadlet_t *data, unsigned int nevents, + unsigned int dbc); + + int encodePortToMBLAEvents(AmdtpAudioPort *, quadlet_t *data, + unsigned int offset, unsigned int nevents); + int encodeSilencePortToMBLAEvents(AmdtpAudioPort *, 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; +}; + +} // end of namespace Streaming + +#endif /* __FFADO_AMDTPTRANSMITSTREAMPROCESSOR__ */ + Index: /branches/api-cleanup/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.h =================================================================== --- /branches/api-cleanup/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.h (revision 750) +++ /branches/api-cleanup/src/libstreaming/amdtp/AmdtpReceiveStreamProcessor.h (revision 750) @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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 channel, unsigned char tag, unsigned char sy, + unsigned int cycle, unsigned int dropped); + enum eChildReturnValue processPacketData(unsigned char *data, unsigned int length, + unsigned char channel, unsigned char tag, unsigned char sy, + unsigned int cycle, unsigned int dropped); + + 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: + bool decodePacketPorts(quadlet_t *data, unsigned int nevents, unsigned int dbc); + int decodeMBLAEventsToPort(AmdtpAudioPort *, quadlet_t *data, unsigned int offset, unsigned int nevents); + + unsigned int getSytInterval(); + + int m_dimension; + unsigned int m_syt_interval; +}; + + +} // end of namespace Streaming + +#endif /* __FFADO_AMDTPRECEIVESTREAMPROCESSOR__ */ + Index: /branches/api-cleanup/src/libstreaming/amdtp/AmdtpPort.cpp =================================================================== --- /branches/api-cleanup/src/libstreaming/amdtp/AmdtpPort.cpp (revision 742) +++ /branches/api-cleanup/src/libstreaming/amdtp/AmdtpPort.cpp (revision 742) @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "AmdtpPort.h" +#include + +namespace Streaming { + +} // end of namespace Streaming Index: /branches/api-cleanup/src/libstreaming/amdtp/AmdtpPortInfo.h =================================================================== --- /branches/api-cleanup/src/libstreaming/amdtp/AmdtpPortInfo.h (revision 742) +++ /branches/api-cleanup/src/libstreaming/amdtp/AmdtpPortInfo.h (revision 742) @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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() {}; + + + int getLocation() {return m_location;}; + int getPosition() {return m_position;}; + enum E_Formats getFormat() {return m_format;}; + +protected: + int m_position; + int m_location; + enum E_Formats m_format; + +}; + +} // end of namespace Streaming + +#endif /* __FFADO_AMDTPPORTINFO__ */ Index: /branches/api-cleanup/src/libstreaming/StreamProcessorManager.cpp =================================================================== --- /branches/api-cleanup/src/libstreaming/StreamProcessorManager.cpp (revision 807) +++ /branches/api-cleanup/src/libstreaming/StreamProcessorManager.cpp (revision 807) @@ -0,0 +1,1030 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#include "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_nb_buffers( 0 ) + , m_period( 0 ) + , m_nominal_framerate ( 0 ) + , m_xrun_happened( false ) + , m_xruns(0) + , m_nbperiods(0) +{ + addOption(Util::OptionContainer::Option("slaveMode",false)); +} + +StreamProcessorManager::StreamProcessorManager(unsigned int period, unsigned int framerate, unsigned int nb_buffers) + : m_is_slave( false ) + , m_SyncSource(NULL) + , m_nb_buffers(nb_buffers) + , m_period(period) + , m_nominal_framerate ( framerate ) + , m_xruns(0) + , m_xrun_happened( false ) + , m_nbperiods(0) +{ + addOption(Util::OptionContainer::Option("slaveMode",false)); +} + +StreamProcessorManager::~StreamProcessorManager() { +} + +/** + * 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"); + } + + // 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_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); + } + } + 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); + } + } + 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"); + int nb_sync_runs=20; + 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); + } + + // STEP X: switch SP's over to the running state + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + 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)->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) + 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; + } + + // now align the received streams + if(!alignReceivedStreams()) { + debugError("Could not align streams\n"); + return false; + } + debugOutput( DEBUG_LEVEL_VERBOSE, " StreamProcessor streams running...\n"); + return true; +} + +bool +StreamProcessorManager::alignReceivedStreams() +{ + if(m_SyncSource == NULL) return false; + 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; + }; + + 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; + } + } + if(!transferSilence()) { + debugError("Could not transfer silence\n"); + return false; + } + 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"); + + // put all SP's into dry-running state + if (!startDryRunning()) { + debugFatal("Could not put SP's in dry-running state\n"); + return false; + } + + // start all SP's synchonized + if (!syncStartAll()) { + 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 state + int cnt = 2000; + bool ready = false; + while (!ready && cnt) { + ready = true; + for ( StreamProcessorVectorIterator it = m_ReceiveProcessors.begin(); + it != m_ReceiveProcessors.end(); + ++it ) { + ready &= ((*it)->isDryRunning() || (*it)->isStopped()); + } + for ( StreamProcessorVectorIterator it = m_TransmitProcessors.begin(); + it != m_TransmitProcessors.end(); + ++it ) { + ready &= ((*it)->isDryRunning() || (*it)->isStopped()); + } + 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 ) { + (*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 running state + cnt = 200; + 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) { + debugOutput(DEBUG_LEVEL_VERBOSE, " Timeout waiting for the SP's to stop\n"); + 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 + */ + + // put all SP's back into dry-running state + if (!startDryRunning()) { + debugFatal("Could not put SP's in dry-running state\n"); + return false; + } + + debugOutput( DEBUG_LEVEL_VERBOSE, "Restarting StreamProcessors...\n"); + // start all SP's synchonized + if (!syncStartAll()) { + 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 an xrun occurred + */ +bool StreamProcessorManager::waitForPeriod() { + if(m_SyncSource == NULL) return false; + bool xrun_occurred = false; + bool period_not_ready = true; + + while(period_not_ready) { + debugOutput( DEBUG_LEVEL_VERBOSE, "waiting for period (%d frames in buffer)...\n", m_SyncSource->getBufferFill()); + if(!m_SyncSource->waitForSignal()) { + debugError("Error waiting for signal\n"); + return false; + } + + unsigned int bufferfill = m_SyncSource->getBufferFill(); + period_not_ready = bufferfill < m_period; + +#ifdef DEBUG + if(period_not_ready) { + debugOutput(DEBUG_LEVEL_VERBOSE, "period is not ready (bufferfill: %u)\n", bufferfill); + } else { + debugOutput(DEBUG_LEVEL_VERBOSE, "period is ready (bufferfill: %u)\n", bufferfill); + } +#endif + + // 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) + } + + // 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(); + debugOutput( DEBUG_LEVEL_VERBOSE, "transfer at %llu ticks...\n", + m_time_of_transfer); + + // this is to notify the client of the delay that we introduced by waiting + m_delayed_usecs = - m_SyncSource->getTimeUntilNextPeriodSignalUsecs(); + debugOutput( 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(); + } + debugOutput( 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()) { + debugWarning("Xrun on RECV SP %p due to ISO side xrun\n",*it); + (*it)->dumpInfo(); + } + if (!((*it)->canClientTransferFrames(m_period))) { + debugWarning("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()) { + debugWarning("Xrun on XMIT SP %p due to ISO side xrun\n",*it); + } + if (!((*it)->canClientTransferFrames(m_period))) { + debugWarning("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() { + debugOutput( 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; + debugOutput( 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; + retval &= transferSilence(StreamProcessor::ePT_Receive); + retval &= transferSilence(StreamProcessor::ePT_Transmit); + 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, " 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); + + 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/api-cleanup/src/libstreaming/StreamProcessorManager.h =================================================================== --- /branches/api-cleanup/src/libstreaming/StreamProcessorManager.h (revision 750) +++ /branches/api-cleanup/src/libstreaming/StreamProcessorManager.h (revision 750) @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +#ifndef __FFADO_STREAMPROCESSORMANAGER__ +#define __FFADO_STREAMPROCESSORMANAGER__ + +#include "generic/Port.h" +#include "generic/StreamProcessor.h" + +#include "debugmodule/debugmodule.h" +#include "libutil/Thread.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: + + StreamProcessorManager(); + StreamProcessorManager(unsigned int period, unsigned int rate, unsigned int nb_buffers); + virtual ~StreamProcessorManager(); + + bool prepare(); ///< to be called after the processors are registered + + bool start(); + bool stop(); + + bool startDryRunning(); + bool syncStartAll(); + + // 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;}; + + 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(); + 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: + + // thread sync primitives + bool m_xrun_happened; + bool m_thread_realtime; + int m_thread_priority; + + // processor list + StreamProcessorVector m_ReceiveProcessors; + StreamProcessorVector m_TransmitProcessors; + + unsigned int m_nb_buffers; + unsigned int m_period; + unsigned int m_nominal_framerate; + unsigned int m_xruns; + + unsigned int m_nbperiods; + + DECLARE_DEBUG_MODULE; + +}; + +} + +#endif /* __FFADO_STREAMPROCESSORMANAGER__ */ + + Index: /branches/api-cleanup/src/libstreaming/generic/PortManager.cpp =================================================================== --- /branches/api-cleanup/src/libstreaming/generic/PortManager.cpp (revision 750) +++ /branches/api-cleanup/src/libstreaming/generic/PortManager.cpp (revision 750) @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "PortManager.h" +#include "Port.h" + +#include + +#include +#include + + +namespace Streaming { + +IMPL_DEBUG_MODULE( PortManager, PortManager, DEBUG_LEVEL_NORMAL ); + +PortManager::PortManager() { + +} + +PortManager::~PortManager() { +// deleteAllPorts(); +} + +// bool PortManager::setPortBuffersize(unsigned int newsize) { +// debugOutput( DEBUG_LEVEL_VERBOSE, "setting port buffer size to %d\n",newsize); +// +// +// for ( PortVectorIterator it = m_Ports.begin(); +// it != m_Ports.end(); +// ++it ) +// { +// if(!(*it)->setBufferSize(newsize)) { +// debugFatal("Could not set buffer size for port %s\n",(*it)->getName().c_str()); +// return false; +// } +// } +// +// return true; //not found +// +// } + +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::addPort(Port *port) +{ + assert(port); + + debugOutput( DEBUG_LEVEL_VERBOSE, "Adding port %s, type: %d, dir: %d, dtype: %d\n", + port->getName().c_str(), port->getPortType(), port->getDirection(), port->getDataType()); + + port->setVerboseLevel(getDebugLevel()); + + if (makeNameUnique(port)) { + m_Ports.push_back(port); + return true; + } else { + return false; + } +} + +bool PortManager::deletePort(Port *port) +{ + assert(port); + debugOutput( DEBUG_LEVEL_VERBOSE, "deleting 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); +// delete *it; + return true; + } + } + + debugOutput( DEBUG_LEVEL_VERBOSE, "port %s not found \n",port->getName().c_str()); + + return false; //not found + +} + +void PortManager::deleteAllPorts() +{ + debugOutput( DEBUG_LEVEL_VERBOSE, "deleting all ports\n"); + + for ( PortVectorIterator it = m_Ports.begin(); + it != m_Ports.end(); + ++it ) + { + m_Ports.erase(it); +// delete *it; + } + + return; + +} + +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",(*it)->getName().c_str()); + return false; + } + } + return true; +} + +bool PortManager::preparePorts() { + debugOutput( DEBUG_LEVEL_VERBOSE, "preparing ports\n"); + + // clear the cache lists + m_PeriodPorts.clear(); + m_PacketPorts.clear(); + + 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; + } + + // now prepare the cache lists + switch((*it)->getSignalType()) { + case Port::E_PacketSignalled: + m_PacketPorts.push_back(*it); + break; + case Port::E_PeriodSignalled: + m_PeriodPorts.push_back(*it); + break; + default: + debugWarning("%s has unsupported port type\n", + (*it)->getName().c_str()); + break; + } + } + return true; +} + +} Index: /branches/api-cleanup/src/libstreaming/generic/StreamProcessor.h =================================================================== --- /branches/api-cleanup/src/libstreaming/generic/StreamProcessor.h (revision 807) +++ /branches/api-cleanup/src/libstreaming/generic/StreamProcessor.h (revision 807) @@ -0,0 +1,513 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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_WaitingToStop, FIXME: this will be needed for the MOTU's + 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;}; + + // 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(); + +public: // constructor/destructor + StreamProcessor(FFADODevice &parent, enum eProcessorType type); + virtual ~StreamProcessor(); +protected: + FFADODevice& m_Parent; + Ieee1394Service& m_1394service; + IsoHandlerManager& m_IsoHandlerManager; + StreamProcessorManager& m_StreamProcessorManager; + +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, + unsigned 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 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 + + unsigned int getSignalPeriod() {return m_signal_period;}; + bool setSignalPeriod(unsigned int p) {m_signal_period=p; return true;}; + /** + * @brief waits for a 'signal' (blocking) + * + * a 'signal' is: + * when type==Receive: + * - one signal_period of frames is present in the buffer + * (received by the iso side) + * - an error has occurred (xrun, iso error, ...) + * when type==Transmit: + * - at least one signal_period of frames are present in the buffer + * (have been written into it by the client) + * - an error occurred + * + * @return true if the 'signal' is available, false if error + */ + bool waitForSignal(); + + /** + * @brief checks for a 'signal' (non-blocking) + * + * a 'signal' is: + * when type==Receive: + * - one signal_period of frames is present in the buffer + * (received by the iso side) + * - an error has occurred (xrun, iso error, ...) + * when type==Transmit: + * - at least one signal_period of frames are present in the buffer + * (have been written into it by the client) + * - an error occurred + * + * @return true if the 'signal' is available, false if not (or error) + */ + bool tryWaitForSignal(); + + /** + * @brief can a SP process (queue, dequeue) packets at this moment? + * + * + * @return true if packet processing makes sense + */ + bool canProcessPackets(); + + /** + * @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 channel, unsigned char tag, + unsigned char sy, unsigned int cycle, + unsigned int dropped) + {debugWarning("call not allowed\n"); return eCRV_Invalid;}; + virtual enum eChildReturnValue processPacketData(unsigned char *data, unsigned int length, + unsigned char channel, unsigned char tag, + unsigned char sy, unsigned int cycle, + unsigned int dropped) + {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, + int cycle, unsigned int dropped, + unsigned int max_length) + {debugWarning("call not allowed\n"); return eCRV_Invalid;}; + virtual enum eChildReturnValue generatePacketData(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, + unsigned int max_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, + int cycle, unsigned int dropped, + unsigned int max_length) + {debugWarning("call not allowed\n"); return eCRV_Invalid;}; + virtual enum eChildReturnValue generateSilentPacketData(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, + unsigned int max_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(AudioPort *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; }; + +// 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_dropped; /// FIXME:debug + uint64_t m_last_dropped; /// FIXME:debug + int m_last_good_cycle; /// FIXME:debug + uint64_t m_last_timestamp; /// last timestamp (in ticks) + uint64_t m_last_timestamp2; /// last timestamp (in ticks) + uint64_t m_last_timestamp_at_period_ticks; + +//--- 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 + */ + int getSyncDelay() {return m_sync_delay;}; + /** + * sets the sync delay + * @param d sync delay + */ + void setSyncDelay(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(); + + int getLastCycle() {return m_last_cycle;}; + + 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); + + /** + * @brief returns the actual frame rate as calculated by the SP's DLL + * @return the actual frame rate as detected by the DLL + */ + float getActualRate() + {return m_data_buffer->getRate();}; + + protected: + float m_ticks_per_frame; + int m_last_cycle; + int m_sync_delay; + private: + bool m_in_xrun; + sem_t m_signal_semaphore; + unsigned int m_signal_period; + unsigned int m_signal_offset; + +public: + // debug stuff + virtual void dumpInfo(); + virtual void setVerboseLevel(int l); + const char *getStateString() + {return ePSToString(getState());}; + const char *getTypeString() + {return ePTToString(getType());}; + StreamStatistics m_PacketStat; + StreamStatistics m_PeriodStat; + StreamStatistics m_WakeupStat; + DECLARE_DEBUG_MODULE; +}; + +} + +#endif /* __FFADO_STREAMPROCESSOR__ */ + + Index: /branches/api-cleanup/src/libstreaming/generic/Port.cpp =================================================================== --- /branches/api-cleanup/src/libstreaming/generic/Port.cpp (revision 750) +++ /branches/api-cleanup/src/libstreaming/generic/Port.cpp (revision 750) @@ -0,0 +1,578 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "Port.h" + +#include +#include + +namespace Streaming { + +IMPL_DEBUG_MODULE( Port, Port, DEBUG_LEVEL_NORMAL ); + +Port::Port(std::string name, enum E_PortType porttype, enum E_Direction direction) + : m_Name(name), + m_SignalType(E_PeriodSignalled), + m_BufferType(E_PointerBuffer), + m_disabled(true), + m_buffersize(0), + m_eventsize(0), + m_DataType(E_Int24), + m_PortType(porttype), + m_Direction(direction), + m_buffer(0), + m_ringbuffer(0), + m_use_external_buffer(false), + m_do_ratecontrol(false), + m_event_interval(0), + m_slot_interval(0), + m_rate_counter(0), + m_rate_counter_minimum(0), + m_average_ratecontrol(false), + m_State(E_Created) +{ + +} + +/** + * 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; + } + + switch (m_BufferType) { + case E_PointerBuffer: + if (m_use_external_buffer) { + // don't do anything + } else if (!allocateInternalBuffer()) { + debugFatal("Could not allocate internal buffer!\n"); + return false; + } + break; + + case E_RingBuffer: + if (m_use_external_buffer) { + debugFatal("Cannot use an external ringbuffer! \n"); + return false; + } else if (!allocateInternalRingBuffer()) { + debugFatal("Could not allocate internal ringbuffer!\n"); + return false; + } + break; + default: + debugFatal("Unsupported buffer type! (%d)\n",(int)m_BufferType); + return false; + break; + } + + m_eventsize=getEventSize(); // this won't change, so cache it + + m_State = E_Initialized; + return true; +} + +bool Port::reset() { + if (m_BufferType==E_RingBuffer) { + ffado_ringbuffer_reset(m_ringbuffer); + } + 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() { + switch (m_DataType) { + case E_Float: + return sizeof(float); + case E_Int24: // 24 bit 2's complement, packed in a 32bit integer (LSB's) + return sizeof(uint32_t); + case E_MidiEvent: + return sizeof(uint32_t); + default: + return 0; + } +} + +bool Port::setDataType(enum E_DataType d) { + debugOutput( DEBUG_LEVEL_VERBOSE, "Setting datatype to %d for port %s\n",(int) d,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; + } + + // do some sanity checks + bool type_is_ok=false; + switch (m_PortType) { + case E_Audio: + if(d == E_Int24) type_is_ok=true; + if(d == E_Float) type_is_ok=true; + break; + case E_Midi: + if(d == E_MidiEvent) type_is_ok=true; + break; + case E_Control: + if(d == E_Default) type_is_ok=true; + break; + default: + break; + } + + if(!type_is_ok) { + debugFatal("Datatype not supported by this type of port!\n"); + return false; + } + + m_DataType=d; + return true; +} + +bool Port::setSignalType(enum E_SignalType s) { + debugOutput( DEBUG_LEVEL_VERBOSE, "Setting signaltype to %d for port %s\n",(int)s,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; + } + + // do some sanity checks + bool type_is_ok=false; + switch (m_PortType) { + case E_Audio: + if(s == E_PeriodSignalled) type_is_ok=true; + break; + case E_Midi: + if(s == E_PacketSignalled) type_is_ok=true; + break; + case E_Control: + if(s == E_PeriodSignalled) type_is_ok=true; + break; + default: + break; + } + if(!type_is_ok) { + debugFatal("Signalling type not supported by this type of port!\n"); + return false; + } + m_SignalType=s; + return true; +} + +bool Port::setBufferType(enum E_BufferType b) { + debugOutput( DEBUG_LEVEL_VERBOSE, "Setting buffer type to %d for port %s\n",(int)b,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; + } + // do some sanity checks + bool type_is_ok=false; + switch (m_PortType) { + case E_Audio: + if(b == E_PointerBuffer) type_is_ok=true; + break; + case E_Midi: + if(b == E_RingBuffer) type_is_ok=true; + break; + case E_Control: + break; + default: + break; + } + if(!type_is_ok) { + debugFatal("Buffer type not supported by this type of port!\n"); + return false; + } + m_BufferType=b; + return true; +} + +bool Port::useExternalBuffer(bool b) { + // If called on an initialised stream but the request isn't for a change silently + // allow it (relied on by C API as used by jack backend driver) + if (m_State==E_Initialized && m_use_external_buffer==b) + return true; + + debugOutput( DEBUG_LEVEL_VERBOSE, "Setting external buffer use to %d for port %s\n",(int)b,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_use_external_buffer=b; + return true; +} + +// buffer handling api's for pointer buffers +/** + * Get the buffer address (being the external or the internal one). + * + * @param buff + */ +void *Port::getBufferAddress() { + assert(m_BufferType==E_PointerBuffer); + return m_buffer; +}; + +/** + * Set the external buffer address. + * only call this when you have specified that you will use + * an external buffer before doing the init() + * + * @param buff + */ +void Port::setExternalBufferAddress(void *buff) { + assert(m_BufferType==E_PointerBuffer); + assert(m_use_external_buffer); // don't call this with an internal buffer! + m_buffer=buff; +}; + +// buffer handling api's for ringbuffers +bool Port::writeEvent(void *event) { + +#ifdef DEBUG + if (m_State != E_Initialized) { + debugFatal("Port (%s) not in E_Initialized state: %d\n",m_Name.c_str(),m_State); + return false; + } + + if(m_BufferType!=E_RingBuffer) { + debugError("operation not allowed on non E_RingBuffer ports\n"); + show(); + return false; + } + assert(m_ringbuffer); +#endif + + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Writing event %08X with size %d to port %s\n",*((quadlet_t *)event),m_eventsize, m_Name.c_str()); + + return (ffado_ringbuffer_write(m_ringbuffer, (char *)event, m_eventsize)==m_eventsize); +} + +bool Port::readEvent(void *event) { + +#ifdef DEBUG + if (m_State != E_Initialized) { + debugFatal("Port (%s) not in E_Initialized state: %d\n",m_Name.c_str(),m_State); + return false; + } + + if(m_BufferType!=E_RingBuffer) { + debugError("operation not allowed on non E_RingBuffer ports\n"); + show(); + return false; + } + assert(m_ringbuffer); +#endif + + + unsigned int read=ffado_ringbuffer_read(m_ringbuffer, (char *)event, m_eventsize); + + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Reading event %X with size %d from port %s\n",*((quadlet_t *)event),m_eventsize,m_Name.c_str()); + + + return (read==m_eventsize); +} + +int Port::writeEvents(void *event, unsigned int nevents) { + +#ifdef DEBUG + if (m_State != E_Initialized) { + debugFatal("Port (%s) not in E_Initialized state: %d\n",m_Name.c_str(),m_State); + return false; + } + + if(m_BufferType!=E_RingBuffer) { + debugError("operation not allowed on non E_RingBuffer ports\n"); + show(); + return false; + } + assert(m_ringbuffer); +#endif + + + unsigned int bytes2write=m_eventsize*nevents; + + unsigned int written=ffado_ringbuffer_write(m_ringbuffer, (char *)event,bytes2write)/m_eventsize; + +#ifdef DEBUG + if(written) { + unsigned int i=0; + quadlet_t * tmp=(quadlet_t *)event; + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "Written %d events (",written); + for (i=0;i= m_eventsize); + + if(byte_present_in_buffer) { + + if(!m_do_ratecontrol) { + return true; + } + + if(m_rate_counter <= 0) { + // update the counter + if(m_average_ratecontrol) { + m_rate_counter += m_event_interval; + assert(m_rate_counterevent_interval) { + debugWarning("Rate control not needed!\n",m_Name.c_str()); + m_do_ratecontrol=false; + return false; + } + if(slot_interval==0) { + debugFatal("Cannot have slot interval == 0!\n"); + m_do_ratecontrol=false; + return false; + } + if(event_interval==0) { + debugFatal("Cannot have event interval == 0!\n"); + m_do_ratecontrol=false; + return false; + } + m_do_ratecontrol=use; + m_event_interval=event_interval; + m_slot_interval=slot_interval; + m_rate_counter=0; + + // NOTE: pretty arbitrary, but in average mode this limits the peak stream rate + m_rate_counter_minimum=-(2*event_interval); + + m_average_ratecontrol=average; + + } else { + debugOutput(DEBUG_LEVEL_VERBOSE, "Disabling rate control for port %s...\n",m_Name.c_str()); + m_do_ratecontrol=use; + } + return true; +} + +/// Enable the port. (this can be called anytime) +void +Port::enable() { + debugOutput(DEBUG_LEVEL_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_VERBOSE, "Disabling port %s...\n",m_Name.c_str()); + m_disabled=false; +}; + + +/* Private functions */ + +bool Port::allocateInternalBuffer() { + int event_size=getEventSize(); + + debugOutput(DEBUG_LEVEL_VERBOSE, + "Allocating internal buffer of %d events with size %d (%s)\n", + m_buffersize, event_size, m_Name.c_str()); + + if(m_buffer) { + debugWarning("already has an internal buffer attached, re-allocating\n"); + freeInternalBuffer(); + } + + m_buffer=calloc(m_buffersize,event_size); + if (!m_buffer) { + debugFatal("could not allocate internal buffer\n"); + m_buffersize=0; + return false; + } + + return true; +} + +void Port::freeInternalBuffer() { + debugOutput(DEBUG_LEVEL_VERBOSE, + "Freeing internal buffer (%s)\n",m_Name.c_str()); + + if(m_buffer) { + free(m_buffer); + m_buffer=0; + } +} + +bool Port::allocateInternalRingBuffer() { + int event_size=getEventSize(); + + debugOutput(DEBUG_LEVEL_VERBOSE, + "Allocating internal buffer of %d events with size %d (%s)\n", + m_buffersize, event_size, m_Name.c_str()); + + if(m_ringbuffer) { + debugWarning("already has an internal ringbuffer attached, re-allocating\n"); + freeInternalRingBuffer(); + } + + m_ringbuffer=ffado_ringbuffer_create(m_buffersize * event_size); + if (!m_ringbuffer) { + debugFatal("could not allocate internal ringbuffer\n"); + m_buffersize=0; + return false; + } + + return true; +} + +void Port::freeInternalRingBuffer() { + debugOutput(DEBUG_LEVEL_VERBOSE, + "Freeing internal ringbuffer (%s)\n",m_Name.c_str()); + + if(m_ringbuffer) { + ffado_ringbuffer_free(m_ringbuffer); + m_ringbuffer=0; + } +} + +void Port::show() { + debugOutput(DEBUG_LEVEL_VERBOSE,"Name : %s\n", m_Name.c_str()); + debugOutput(DEBUG_LEVEL_VERBOSE,"Signal Type : %d\n", m_SignalType); + debugOutput(DEBUG_LEVEL_VERBOSE,"Buffer Type : %d\n", m_BufferType); + 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", m_eventsize); + debugOutput(DEBUG_LEVEL_VERBOSE,"Data Type : %d\n", m_DataType); + debugOutput(DEBUG_LEVEL_VERBOSE,"Port Type : %d\n", m_PortType); + debugOutput(DEBUG_LEVEL_VERBOSE,"Direction : %d\n", m_Direction); + debugOutput(DEBUG_LEVEL_VERBOSE,"Rate Control? : %d\n", m_do_ratecontrol); +} + +void Port::setVerboseLevel(int l) { + setDebugLevel(l); +} + +} Index: /branches/api-cleanup/src/libstreaming/generic/PortManager.h =================================================================== --- /branches/api-cleanup/src/libstreaming/generic/PortManager.h (revision 742) +++ /branches/api-cleanup/src/libstreaming/generic/PortManager.h (revision 742) @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +#ifndef __FFADO_PORTMANAGER__ +#define __FFADO_PORTMANAGER__ + +#include "Port.h" + +#include "debugmodule/debugmodule.h" + +#include + +namespace Streaming { + +class Port; +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 addPort(Port *port); + virtual bool deletePort(Port *port); + virtual void deleteAllPorts(); + + int getPortCount(enum Port::E_PortType); + int getPortCount(); + +// virtual bool setPortBuffersize(unsigned int newsize); + + Port *getPortAtIdx(unsigned int index); + + virtual bool resetPorts(); + virtual bool initPorts(); + virtual bool preparePorts(); + + virtual void setVerboseLevel(int l); + +protected: + PortVector m_Ports; + PortVector m_PacketPorts; + PortVector m_PeriodPorts; +// PortVector m_SamplePorts; + + DECLARE_DEBUG_MODULE; + +}; + +} + +#endif /* __FFADO_PORTMANAGER__ */ + + Index: /branches/api-cleanup/src/libstreaming/generic/Port.h =================================================================== --- /branches/api-cleanup/src/libstreaming/generic/Port.h (revision 742) +++ /branches/api-cleanup/src/libstreaming/generic/Port.h (revision 742) @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +#ifndef __FFADO_PORT__ +#define __FFADO_PORT__ + +#include "libutil/ringbuffer.h" + +#include "debugmodule/debugmodule.h" + +#include +#include + +namespace Streaming { + +/*! +\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. + + \todo rework the implementation into something more beautifull +*/ +class Port { + +public: + friend class PortManager; + + /* + * IMPORTANT: if you add something to any of these enum's, be sure to + * check the code where they are used. + */ + + /*! + \brief Specifies the buffer type for ports + + A PointerBuffer uses the getBufferAddress() and setBufferAddres() interface + A Ringbuffer uses the read/write interface + */ + enum E_BufferType { + E_PointerBuffer, + E_RingBuffer + }; + + /*! + \brief Specifies the signalling type for ports + */ + enum E_SignalType { + E_PacketSignalled, ///< the port is to be processed for every packet + E_PeriodSignalled, ///< the port is to be processed after a period of frames +// E_SampleSignalled ///< the port is to be processed after each frame (sample) + }; + + /*! + \brief The datatype of the port buffer + */ + enum E_DataType { + E_Float, + E_Int24, + E_MidiEvent, + E_Default, + }; + + /*! + \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(std::string name, enum E_PortType porttype, enum E_Direction 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(); + + /** + * \brief sets the event type for the port buffer + * + * \note use before calling init() + */ + virtual bool setDataType(enum E_DataType); + + enum E_DataType getDataType() {return m_DataType;}; + + /** + * \brief sets the event type for the port buffer + * + * \note use before calling init() + */ + virtual bool setSignalType(enum E_SignalType ); + + enum E_SignalType getSignalType() {return m_SignalType;}; ///< returns the signalling type of the port + + /** + * \brief sets the buffer type for the port + * + * \note use before calling init() + */ + virtual bool setBufferType(enum E_BufferType ); + + enum E_BufferType getBufferType() {return m_BufferType;}; ///< returns the buffer type of the port + + 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); + + /** + * \brief use an external buffer (or not) + * + * \note use before calling init() + */ + virtual bool useExternalBuffer(bool b); + + void setExternalBufferAddress(void *buff); + + + /** + * \brief enable/disable ratecontrol + * + * Rate control is nescessary for some types of ports (most notably + * midi). The StreamProcessor that handles the port should call canRead() + * everytime a 'slot' that could be filled with an event passes. The canRead + * function will return true if + * (1) there is an event ready in the buffer + * (2) we are allowed to send an event in this slot + * + * Setting the rate works is done with the slot_interval and the event_interval + * parameters. On every call to canRead(), a counter is decremented with + * slot_interval. If the counter drops below 0, canRead() returns true and resets + * the counter to event_interval. + * + * e.g. for AMDTP midi, we are only allowed to send a midi byte every 320us + * if the SYT interval is 8, there is exactly one midi slot every packet. + * therefore the slot_interval is 1/8000s (=125us), and the event_interval + * is 320us. + * + * Note that the interval parameters are unitless, so you can adapt them + * to your needs. In the AMDTP case for example, when the SYT interval is 32 + * (when the samplerate is 192kHz for example) there are 4 midi slots in + * each packet, making the slot time interval 125us/4 = 31.25us. + * The event time interval stays the same (320us). We can however set the + * slot_interval to 3125 and the event_interval to 32000, as we can choose + * the unit of the counter time step (chosen to be 10ns in this case). + * + * The average argument deserves some attention too. If average is true, we use + * average rate control. This means that on average there will be a delay of + * event_interval between two events, but that sometimes there can be a smaller + * delay. This mode fixes the average rate of the stream. + * If average is false, there will always be a minimal delay of event_interval + * between two events. This means that the maximum rate of the stream is fixed, + * and that the average rate will be lower than (or at max equal to) the rate in + * average mode. + * + * + * \note only works for the E_RingBuffer ports + * \note use before calling init() + * + * @param use set this to true to use rate control + * @param slot_interval the interval between slots + * @param event_interval the interval between events + * @param average use average rate control + * @return true if rate control was enabled/disabled successfully + */ + virtual bool useRateControl(bool use, unsigned int slot_interval, + unsigned int event_interval, bool average); + + bool usingRateControl() { return m_do_ratecontrol;}; ///< are we using rate control? + + /** + * Can we send an event in this slot. subject to rate control and + * byte availability. + * @return true if we can send an event on this slot + */ + bool canRead(); + + // FIXME: this is not really OO, but for performance??? + void *getBufferAddress(); + + // TODO: extend this with a blocking interface + bool writeEvent(void *event); ///< write one event + bool readEvent(void *event); ///< read one event + int writeEvents(void *event, unsigned int nevents); ///< write multiple events + int readEvents(void *event, unsigned int nevents); ///< read multiple events + + virtual void setVerboseLevel(int l); + virtual void show(); + +protected: + std::string m_Name; ///< Port name, [at construction] + + enum E_SignalType m_SignalType; ///< Signalling type, [at construction] + enum E_BufferType m_BufferType; ///< Buffer type, [at construction] + + bool m_disabled; ///< is the port disabled?, [anytime] + + unsigned int m_buffersize; + unsigned int m_eventsize; + + enum E_DataType m_DataType; + enum E_PortType m_PortType; + enum E_Direction m_Direction; + + void *m_buffer; + ffado_ringbuffer_t *m_ringbuffer; + bool m_use_external_buffer; + + bool m_do_ratecontrol; + int m_event_interval; + int m_slot_interval; + int m_rate_counter; + int m_rate_counter_minimum; + bool m_average_ratecontrol; + + bool allocateInternalBuffer(); + void freeInternalBuffer(); + + bool allocateInternalRingBuffer(); + void freeInternalRingBuffer(); + + 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(std::string name, enum E_Direction direction) + : Port(name, E_Audio, direction) + {}; + + virtual ~AudioPort() {}; + +protected: + + +}; + +/*! +\brief The Base Class for a Midi Port + + +*/ +class MidiPort : public Port { + +public: + + MidiPort(std::string name, enum E_Direction direction) + : Port(name, E_Midi, direction) + {}; + virtual ~MidiPort() {}; + + +protected: + + +}; + +/*! +\brief The Base Class for a control port + + +*/ +class ControlPort : public Port { + +public: + + ControlPort(std::string name, enum E_Direction direction) + : Port(name, E_Control, direction) + {}; + virtual ~ControlPort() {}; + + +protected: + + +}; + +} + +#endif /* __FFADO_PORT__ */ + + Index: /branches/api-cleanup/src/libstreaming/generic/StreamProcessor.cpp =================================================================== --- /branches/api-cleanup/src/libstreaming/generic/StreamProcessor.cpp (revision 807) +++ /branches/api-cleanup/src/libstreaming/generic/StreamProcessor.cpp (revision 807) @@ -0,0 +1,1830 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "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 POST_SEMAPHORE { \ + int tmp; \ + sem_getvalue(&m_signal_semaphore, &tmp); \ + debugWarning("posting semaphore from value %d\n", tmp); \ + sem_post(&m_signal_semaphore); \ +} +*/ + +#define POST_SEMAPHORE { \ + sem_post(&m_signal_semaphore); \ +} + +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_channel( -1 ) + , m_dropped(0) + , m_last_timestamp(0) + , m_last_timestamp2(0) + , m_scratch_buffer( NULL ) + , m_scratch_buffer_size_bytes( 0 ) + , m_ticks_per_frame( 0 ) + , m_last_cycle( -1 ) + , m_sync_delay( 0 ) + , m_in_xrun( false ) + , m_signal_period( 0 ) + , m_signal_offset( 0 ) +{ + // 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; + sem_destroy(&m_signal_semaphore); +} + +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 ISOHANDLER_PER_HANDLER_THREAD + // 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())); + debugOutput(DEBUG_LEVEL_VERBOSE, "Nominal prebuffer: %u\n", packets_to_prebuffer); + return packets_to_prebuffer; +#else + // the target is to have all of the transmit buffer (at period transfer) as ISO packets + // when one period is received, there will be approx (NbBuffers - 1) * period_size frames + // in the transmit buffer (the others are still to be put into the xmit frame buffer) + unsigned int packets_to_prebuffer = (getPacketsPerPeriod() * (m_StreamProcessorManager.getNbBuffers()-1)); + debugOutput(DEBUG_LEVEL_VERBOSE, "Nominal prebuffer: %u\n", packets_to_prebuffer); + + // however we have to take into account the fact that there is some sync delay + // we assume that the SPM has indicated + // HACK: this counts on the fact that the latency for this stream will be the same as the + // latency for the receive sync source + unsigned int est_sync_delay = getPacketsPerPeriod() / MINIMUM_INTERRUPTS_PER_PERIOD; + est_sync_delay += STREAMPROCESSORMANAGER_SIGNAL_DELAY_TICKS / TICKS_PER_CYCLE; + packets_to_prebuffer -= est_sync_delay; + debugOutput(DEBUG_LEVEL_VERBOSE, " correct for sync delay (%d): %u\n", + est_sync_delay, + packets_to_prebuffer); + + // only queue a part of the theoretical max in order not to have too much 'not ready' cycles + packets_to_prebuffer = (packets_to_prebuffer * MAX_ISO_XMIT_BUFFER_FILL_PCT * 1000) / 100000; + debugOutput(DEBUG_LEVEL_VERBOSE, " reduce to %d%%: %u\n", + MAX_ISO_XMIT_BUFFER_FILL_PCT, packets_to_prebuffer); + + return packets_to_prebuffer; +#endif +} + +/*********************************************** + * 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(int d) { + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "Setting SP %p SyncDelay to %d ticks\n", this, d); + m_sync_delay = d; +} + +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(); +} + +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) { + debugWarning("(%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, + unsigned int cycle, unsigned int dropped) { + if(m_last_cycle == -1) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Handler for %s SP %p is alive (cycle = %u)\n", getTypeString(), this, cycle); + } + + int dropped_cycles = 0; + if (m_last_cycle != (int)cycle && m_last_cycle != -1) { + dropped_cycles = diffCycles(cycle, m_last_cycle) - 1; + if (dropped_cycles < 0) { + debugWarning("(%p) dropped < 1 (%d), cycle: %d, last_cycle: %d, dropped: %d\n", + this, dropped_cycles, cycle, m_last_cycle, dropped); + } + if (dropped_cycles > 0) { + debugWarning("(%p) dropped %d packets on cycle %u, 'dropped'=%u, cycle=%d, m_last_cycle=%d\n", + this, dropped_cycles, cycle, dropped, cycle, m_last_cycle); + m_dropped += dropped_cycles; + m_in_xrun = true; + m_last_cycle = cycle; + POST_SEMAPHORE; + return RAW1394_ISO_DEFER; + //flushDebugOutput(); + //assert(0); + } + } + m_last_cycle = cycle; + + // bypass based upon state + if (m_state == ePS_Invalid) { + debugError("Should not have state %s\n", ePSToString(m_state) ); + POST_SEMAPHORE; + return RAW1394_ISO_ERROR; + } + 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, 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"); + POST_SEMAPHORE; + return RAW1394_ISO_ERROR; + } + } else { + // not time to disable yet + } + // the received data can be discarded while waiting for the stream + // to be disabled + return RAW1394_ISO_OK; + } + + // check whether we are waiting for a stream to be enabled + else if(m_state == ePS_WaitingForStreamEnable) { + // we then check whether we have to switch on this cycle + if (diffCycles(cycle, 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"); + POST_SEMAPHORE; + 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, channel, tag, sy, cycle, dropped_cycles); + if (result == eCRV_OK) { + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "RECV: CY=%04u TS=%011llu\n", + cycle, m_last_timestamp); + // update some accounting + m_last_good_cycle = cycle; + m_last_dropped = dropped_cycles; + + // 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, 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"); + POST_SEMAPHORE; + 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"); + POST_SEMAPHORE; + return RAW1394_ISO_ERROR; + } + } + + // handle dropped cycles + if(dropped_cycles) { + // they represent a discontinuity in the timestamps, and hence are + // to be dealt with + debugWarning("(%p) Correcting timestamp for dropped cycles, discarding packet...\n", this); + m_data_buffer->setBufferTailTimestamp(m_last_timestamp); + if (m_state == ePS_Running) { + // this is an xrun situation + m_in_xrun = true; + debugWarning("Should update state to WaitingForStreamDisable due to dropped packet xrun\n"); + m_cycle_to_switch_state = cycle + 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"); + POST_SEMAPHORE; + return RAW1394_ISO_ERROR; + } + POST_SEMAPHORE; + return RAW1394_ISO_DEFER; + } + } + + // for all states that reach this we are allowed to + // do protocol specific data reception + enum eChildReturnValue result2 = processPacketData(data, length, channel, tag, sy, cycle, dropped_cycles); + + // if an xrun occured, switch to the dryRunning state and + // allow for the xrun to be picked up + if (result2 == eCRV_XRun) { + debugWarning("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+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"); + POST_SEMAPHORE; + return RAW1394_ISO_ERROR; + } + POST_SEMAPHORE; + return RAW1394_ISO_DEFER; + } else if(result2 == eCRV_OK) { + // no problem here + // if we have enough samples, we can post the semaphore and + // defer further processing until later. this will allow us to + // run the client and process the frames such that we can put them + // into the xmit buffers ASAP + if (m_state == ePS_Running) { + unsigned int bufferfill = m_data_buffer->getBufferFill(); + if(bufferfill >= m_signal_period + m_signal_offset) { + // this to avoid multiple signals for the same period + int semval; + sem_getvalue(&m_signal_semaphore, &semval); + unsigned int signal_period = m_signal_period * (semval + 1) + m_signal_offset; + if(bufferfill >= signal_period) { + debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) buffer fill (%d) > signal period (%d), sem_val=%d\n", + this, m_data_buffer->getBufferFill(), signal_period, semval); + POST_SEMAPHORE; + } + // the process thread should have higher prio such that we are blocked until + // the samples are processed. + } + } + return RAW1394_ISO_OK; + } else { + debugError("Invalid response\n"); + POST_SEMAPHORE; + 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"); + POST_SEMAPHORE; + return RAW1394_ISO_ERROR; + } + debugError("reached the unreachable\n"); + POST_SEMAPHORE; + return RAW1394_ISO_ERROR; +} + +enum raw1394_iso_disposition +StreamProcessor::getPacket(unsigned char *data, unsigned int *length, + unsigned char *tag, unsigned char *sy, + int cycle, unsigned int dropped, unsigned int max_length) { + if (cycle<0) { + *tag = 0; + *sy = 0; + *length = 0; + return RAW1394_ISO_OK; + } + + unsigned int ctr; + int now_cycles; + int cycle_diff; + + if(m_last_cycle == -1) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Handler for %s SP %p is alive (cycle = %d)\n", getTypeString(), this, cycle); + } + + int dropped_cycles = 0; + if (m_last_cycle != cycle && m_last_cycle != -1) { + dropped_cycles = diffCycles(cycle, m_last_cycle) - 1; + if (dropped_cycles < 0) { + debugWarning("(%p) dropped < 1 (%d), cycle: %d, last_cycle: %d, dropped: %d\n", + this, dropped_cycles, cycle, m_last_cycle, dropped); + } + if (dropped_cycles > 0) { + debugWarning("(%p) dropped %d packets on cycle %u (last_cycle=%u, dropped=%d)\n", this, dropped_cycles, cycle, m_last_cycle, dropped); + m_dropped += dropped_cycles; + // 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); + debugWarning("dropped packets xrun\n"); + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to WaitingForStreamDisable due to dropped packets xrun\n"); + 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; + } + } + } + if (cycle >= 0) { + m_last_cycle = cycle; + } + + // bypass based upon state + if (m_state == ePS_Invalid) { + debugError("Should not have state %s\n", ePSToString(m_state) ); + return RAW1394_ISO_ERROR; + } + if (m_state == ePS_Created) { + *tag = 0; + *sy = 0; + *length = 0; + return RAW1394_ISO_DEFER; + } + + // normal processing + // note that we can't use getCycleTimer directly here, + // because packets are queued in advance. This means that + // we the packet we are constructing will be sent out + // on 'cycle', not 'now'. + ctr = m_1394service.getCycleTimer(); + now_cycles = (int)CYCLE_TIMER_GET_CYCLES(ctr); + + // the difference between the cycle this + // packet is intended for and 'now' + cycle_diff = diffCycles(cycle, now_cycles); + + if(cycle_diff < 0 && (m_state == ePS_Running || m_state == ePS_DryRunning)) { + debugWarning("Requesting packet for cycle %04d which is in the past (now=%04dcy)\n", + cycle, now_cycles); + if(m_state == ePS_Running) { + debugShowBackLogLines(200); +// flushDebugOutput(); +// assert(0); + debugWarning("generatePacketData xrun\n"); + m_in_xrun = true; + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to WaitingForStreamDisable due to data xrun\n"); + 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; + } + } + + // 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, 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 + } + } + // check whether we are waiting for a stream to be enabled + else if(m_state == ePS_WaitingForStreamEnable) { + // we then check whether we have to switch on this cycle + if (diffCycles(cycle, 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) { + // as long as the cycle parameter is not in sync with + // the current time, the stream is considered not + // to be 'running' + // we then check whether we have to switch on this cycle + if ((cycle_diff >= 0) && (diffCycles(cycle, 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, cycle, dropped_cycles, max_length); + if (result == eCRV_Packet || result == eCRV_Defer) { + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "XMIT: CY=%04u TS=%011llu\n", + cycle, m_last_timestamp); + // update some accounting + m_last_good_cycle = cycle; + m_last_dropped = dropped_cycles; + + // 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, tag, sy, cycle, dropped_cycles, max_length); + // if an xrun occured, switch to the dryRunning state and + // allow for the xrun to be picked up + if (result2 == eCRV_XRun) { + debugWarning("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+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; + } + // 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 + debugWarning("generatePacketHeader xrun\n"); + m_in_xrun = true; + debugOutput(DEBUG_LEVEL_VERBOSE, "Should update state to WaitingForStreamDisable due to header xrun\n"); + 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); + 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; +// generateSilentPacketHeader(data, length, tag, sy, cycle, dropped_cycles, max_length); +// generateSilentPacketData(data, length, tag, sy, cycle, dropped_cycles, max_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; + } + } + + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "XMIT EMPTY: CY=%04u\n", cycle); + generateSilentPacketHeader(data, length, tag, sy, cycle, dropped_cycles, max_length); + generateSilentPacketData(data, length, tag, sy, cycle, dropped_cycles, max_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) { + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "%p.getFrames(%d, %11llu)", nbframes, ts); + assert( getType() == ePT_Receive ); + if(isDryRunning()) return getFramesDry(nbframes, ts); + else return getFramesWet(nbframes, ts); +} + +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); + debugOutput( 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 + debugWarning( "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 + debugWarning( "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) +{ + debugOutput( 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) +{ + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "StreamProcessor::dropFrames(%d, %lld)\n", nbframes, ts); + return m_data_buffer->dropFrames(nbframes); +} + +bool StreamProcessor::putFrames(unsigned int nbframes, int64_t ts) +{ + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "%p.putFrames(%d, %11llu)", nbframes, ts); + assert( getType() == ePT_Transmit ); + + if(isDryRunning()) return putFramesDry(nbframes, ts); + else return putFramesWet(nbframes, ts); +} + +bool +StreamProcessor::putFramesWet(unsigned int nbframes, int64_t ts) +{ + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, "StreamProcessor::putFramesWet(%d, %llu)\n", nbframes, ts); + // transfer the data + m_data_buffer->blockProcessWriteFrames(nbframes, ts); + debugOutput(DEBUG_LEVEL_ULTRA_VERBOSE, " New timestamp: %llu\n", ts); + + unsigned int bufferfill = m_data_buffer->getBufferFill(); + if (bufferfill >= m_signal_period + m_signal_offset) { + debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) sufficient frames in buffer (%d / %d), posting semaphore\n", + this, bufferfill, m_signal_period + m_signal_offset); + POST_SEMAPHORE; + } else { + debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) insufficient frames in buffer (%d / %d), not posting semaphore\n", + this, bufferfill, m_signal_period + m_signal_offset); + } + return true; // FIXME: what about failure? +} + +bool +StreamProcessor::putFramesDry(unsigned int nbframes, int64_t ts) +{ + debugOutput(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_ULTRA_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; + } + + unsigned int bufferfill = m_data_buffer->getBufferFill(); + if (bufferfill >= m_signal_period + m_signal_offset) { + debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) sufficient frames in buffer (%d / %d), posting semaphore\n", + this, bufferfill, m_signal_period + m_signal_offset); + POST_SEMAPHORE; + } else { + debugOutput(DEBUG_LEVEL_VERBOSE, "(%p) insufficient frames in buffer (%d / %d), not posting semaphore\n", + this, bufferfill, m_signal_period + m_signal_offset); + } + + return true; +} + +bool +StreamProcessor::waitForSignal() +{ + int result; + if(m_state == ePS_Running) { + result = sem_wait(&m_signal_semaphore); +#ifdef DEBUG + int tmp; + sem_getvalue(&m_signal_semaphore, &tmp); + debugOutput(DEBUG_LEVEL_VERBOSE, " sem_wait returns: %d, sem_value: %d\n", result, tmp); +#endif + return result == 0; + } else { + // when we're not running, we can always provide frames + debugOutput(DEBUG_LEVEL_VERBOSE, "Not running...\n"); + return true; + } +} + +bool +StreamProcessor::tryWaitForSignal() +{ + if(m_state == ePS_Running) { + return sem_trywait(&m_signal_semaphore) == 0; + } else { + // when we're not running, we can always provide frames + debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Not running...\n"); + return true; + } +} + +bool +StreamProcessor::canProcessPackets() +{ + if(m_state != ePS_Running) return true; + bool result; + int bufferfill; + if(getType() == ePT_Receive) { + bufferfill = m_data_buffer->getBufferSpace(); + } else { + bufferfill = m_data_buffer->getBufferFill(); + } + result = bufferfill > getNominalFramesPerPacket(); + debugOutput(DEBUG_LEVEL_VERBOSE, "(%p, %s) for a bufferfill of %d, we return %d\n", + this, ePTToString(getType()), bufferfill, result); + return result; +} + +bool +StreamProcessor::shiftStream(int nbframes) +{ + if(nbframes == 0) return true; + if(nbframes > 0) { + return m_data_buffer->dropFrames(nbframes); + } else { + bool result = true; + while(nbframes++) { + result &= m_data_buffer->writeDummyFrame(); + } + 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_PeriodPorts.begin(); + it != m_PeriodPorts.end(); + ++it ) { + if((*it)->isDisabled()) {continue;}; + + //FIXME: make this into a static_cast when not DEBUG? + Port *port=dynamic_cast(*it); + + switch(port->getPortType()) { + + case Port::E_Audio: + if(provideSilenceToPort(static_cast(*it), offset, nevents)) { + debugWarning("Could not put silence into to port %s",(*it)->getName().c_str()); + no_problem=false; + } + break; + // midi is a packet based port, don't process + // case MotuPortInfo::E_Midi: + // break; + + default: // ignore + break; + } + } + return no_problem; +} + +int +StreamProcessor::provideSilenceToPort( + AudioPort *p, unsigned int offset, unsigned int nevents) +{ + unsigned int j=0; + switch(p->getDataType()) { + default: + case Port::E_Int24: + { + quadlet_t *buffer=(quadlet_t *)(p->getBufferAddress()); + assert(nevents + offset <= p->getBufferSize()); + buffer+=offset; + + for(j = 0; j < nevents; j += 1) { // decode max nsamples + *(buffer)=0; + buffer++; + } + } + break; + case Port::E_Float: + { + float *buffer=(float *)(p->getBufferAddress()); + assert(nevents + offset <= p->getBufferSize()); + buffer+=offset; + + for(j = 0; j < nevents; j += 1) { // decode max nsamples + *buffer = 0.0; + buffer++; + } + } + break; + } + return 0; +} + +/*********************************************** + * State related API * + ***********************************************/ +bool StreamProcessor::init() +{ + debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "init...\n"); + + if (sem_init(&m_signal_semaphore, 0, 0) == -1) { + debugError("Could not init signal semaphore"); + return false; + } + + 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; + } + + 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; + 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); + 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)); + 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 { + 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); + 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)); + return scheduleStateTransition(ePS_WaitingForStreamEnable, tx); +} + +bool StreamProcessor::scheduleStopDryRunning(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); + 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)); + + return scheduleStateTransition(ePS_Stopped, tx); +} + +bool StreamProcessor::scheduleStopRunning(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); + 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)); + return scheduleStateTransition(ePS_WaitingForStreamDisable, tx); +} + +bool StreamProcessor::startDryRunning(int64_t t) { + 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(!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(!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(!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); + // object just created + result = m_data_buffer->init(); + + // prepare the framerate estimate + ticks_per_frame = (TICKS_PER_SECOND*1.0) / ((float)m_StreamProcessorManager.getNominalRate()); + m_ticks_per_frame = ticks_per_frame; + 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 + + // 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; + } + switch ((*it)->getPortType()) { + case Port::E_Audio: + if(!(*it)->setSignalType(Port::E_PeriodSignalled)) { + debugFatal("Could not set signal type to PeriodSignalling"); + return false; + } + // buffertype and datatype are dependant on the API + debugWarning("---------------- ! Doing hardcoded dummy setup ! --------------\n"); + // buffertype and datatype are dependant on the API + if(!(*it)->setBufferType(Port::E_PointerBuffer)) { + debugFatal("Could not set buffer type"); + return false; + } + if(!(*it)->useExternalBuffer(true)) { + debugFatal("Could not set external buffer usage"); + return false; + } + if(!(*it)->setDataType(Port::E_Float)) { + debugFatal("Could not set data type"); + return false; + } + break; + case Port::E_Midi: + if(!(*it)->setSignalType(Port::E_PacketSignalled)) { + debugFatal("Could not set signal type to PacketSignalling"); + return false; + } + // buffertype and datatype are dependant on the API + debugWarning("---------------- ! Doing hardcoded test setup ! --------------\n"); + // buffertype and datatype are dependant on the API + if(!(*it)->setBufferType(Port::E_RingBuffer)) { + debugFatal("Could not set buffer type"); + return false; + } + if(!(*it)->setDataType(Port::E_MidiEvent)) { + debugFatal("Could not set data type"); + return false; + } + break; + default: + debugWarning("Unsupported port type specified\n"); + break; + } + } + // 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 + result &= PortManager::initPorts(); + + 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 + 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 + 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 at cycle %d\n", this, m_last_cycle); + 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 + 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 + + sem_init(&m_signal_semaphore, 0, 0); + m_signal_period = m_StreamProcessorManager.getPeriodSize(); + m_signal_offset = 0; // FIXME: we have to ensure that everyone is ready + + 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; + } + if (m_data_buffer->getBufferFill() >= m_signal_period + m_signal_offset) { + POST_SEMAPHORE; + } + } + + 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 + 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 at cycle %d\n", + this, m_last_cycle); + m_in_xrun = false; + 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 + 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 + + // we have to wake the iterator if it's asleep + POST_SEMAPHORE; + 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 + 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_Stopped) { + 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)); + return false; +updateState_exit_change_failed: + debugError("State transition failed: %s => %s\n", + ePSToString(m_state), ePSToString(next_state)); + return false; +} + +/*********************************************** + * Helper routines * + ***********************************************/ +bool +StreamProcessor::transferSilence(unsigned int nframes) +{ + bool retval; + signed int fc; + ffado_timestamp_t ts_tail_tmp; + + // prepare a buffer of silence + char *dummybuffer = (char *)calloc(getEventSize(), nframes * getEventsPerFrame()); + transmitSilenceBlock(dummybuffer, nframes, 0); + + m_data_buffer->getBufferTailTimestamp(&ts_tail_tmp, &fc); + if (fc != 0) { + debugWarning("Prefilling a buffer that already contains %d frames\n", fc); + } + + // 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() +{ + 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)); + m_data_buffer->dumpInfo(); +} + +void +StreamProcessor::setVerboseLevel(int l) { + setDebugLevel(l); + PortManager::setVerboseLevel(l); + m_data_buffer->setVerboseLevel(l); +} + +} // end of namespace Index: /branches/api-cleanup/src/libstreaming/util/cip.h =================================================================== --- /branches/api-cleanup/src/libstreaming/util/cip.h (revision 742) +++ /branches/api-cleanup/src/libstreaming/util/cip.h (revision 742) @@ -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 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 . + * + */ + +#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 dbs : 8; + uint8_t eoh0 : 2; + uint8_t sid : 6; + + uint8_t dbc : 8; + uint8_t fn : 2; + uint8_t qpc : 3; + uint8_t sph : 1; + uint8_t reserved : 2; + + /* Second quadlet */ + uint8_t fdf : 8; + uint8_t eoh1 : 2; + uint8_t fmt : 6; + + 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/api-cleanup/src/libstreaming/util/cip.c =================================================================== --- /branches/api-cleanup/src/libstreaming/util/cip.c (revision 754) +++ /branches/api-cleanup/src/libstreaming/util/cip.c (revision 754) @@ -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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "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/api-cleanup/src/threads.h =================================================================== --- /branches/api-cleanup/src/threads.h (revision 742) +++ /branches/api-cleanup/src/threads.h (revision 742) @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef THREADS_H +#define THREADS_H + +#include + +class Functor +{ +public: + Functor() {} + virtual ~Functor() {} + + virtual void operator() () = 0; +}; + +//////////////////////////////////////////////////////////////////////// + +template< typename CalleePtr, typename MemFunPtr > +class MemberFunctor0 + : public Functor +{ +public: + MemberFunctor0( const CalleePtr& pCallee, + MemFunPtr pMemFun, + bool bDelete = true ) + : m_pCallee( pCallee ) + , m_pMemFun( pMemFun ) + , m_pSem( 0 ) + , m_bDelete( bDelete ) + {} + + MemberFunctor0( const CalleePtr& pCallee, + MemFunPtr pMemFun, + sem_t* pSem, + bool bDelete = true ) + : m_pCallee( pCallee ) + , m_pMemFun( pMemFun ) + , m_pSem( pSem ) + , m_bDelete( bDelete ) + {} + + virtual ~MemberFunctor0() + {} + + virtual void operator() () + { + ( ( *m_pCallee ).*m_pMemFun )(); + if ( m_pSem ) { + sem_post( m_pSem); + } + if (m_bDelete) { + delete this; + } + } + +private: + CalleePtr m_pCallee; + MemFunPtr m_pMemFun; + sem_t* m_pSem; + bool m_bDelete; +}; + +template< typename CalleePtr, typename MemFunPtr, typename Parm0 > +class MemberFunctor1 + : public Functor +{ +public: + MemberFunctor1( const CalleePtr& pCallee, + MemFunPtr pMemFun, + Parm0 parm0, + bool bDelete = true) + : m_pCallee( pCallee ) + , m_pMemFun( pMemFun ) + , m_parm0( parm0 ) + , m_pSem( 0 ) + , m_bDelete( bDelete ) + {} + + MemberFunctor1( const CalleePtr& pCallee, + MemFunPtr pMemFun, + Parm0 parm0, + sem_t* pSem, + bool bDelete = true ) + : m_pCallee( pCallee ) + , m_pMemFun( pMemFun ) + , m_parm0( parm0 ) + , m_pSem( 0 ) + , m_bDelete( bDelete ) + {} + virtual ~MemberFunctor1() + {} + + virtual void operator() () + { + ( ( *m_pCallee ).*m_pMemFun )( m_parm0 ); + if ( m_pSem ) { + sem_post( m_pSem); + } + if (m_bDelete) { + delete this; + } + } + +private: + CalleePtr m_pCallee; + MemFunPtr m_pMemFun; + Parm0 m_parm0; + sem_t* m_pSem; + bool m_bDelete; +}; + +#endif + + Index: /branches/api-cleanup/src/rme/rme_avdevice.h =================================================================== --- /branches/api-cleanup/src/rme/rme_avdevice.h (revision 742) +++ /branches/api-cleanup/src/rme/rme_avdevice.h (revision 742) @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 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 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 . + * + */ + +#ifndef RMEDEVICE_H +#define RMEDEVICE_H + +#include "ffadodevice.h" + +#include "debugmodule/debugmodule.h" +#include "libavc/avc_definitions.h" + +// #include "libstreaming/rme/RmeStreamProcessor.h" + +class ConfigRom; +class Ieee1394Service; + +namespace Rme { + +// struct to define the supported devices +struct VendorModelEntry { + unsigned int vendor_id; + unsigned int model_id; + char *vendor_name; + char *model_name; +}; + +class RmeDevice : public FFADODevice { +public: + + RmeDevice( Ieee1394Service& ieee1394Service, + std::auto_ptr( configRom )); + virtual ~RmeDevice(); + + 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 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); + +protected: + struct VendorModelEntry *m_model; +}; + +} + +#endif Index: /branches/api-cleanup/src/rme/rme_avdevice.cpp =================================================================== --- /branches/api-cleanup/src/rme/rme_avdevice.cpp (revision 742) +++ /branches/api-cleanup/src/rme/rme_avdevice.cpp (revision 742) @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2005-2007 by Jonathan Woithe + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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 + +#include +#include + +#include + +namespace Rme { + +// to define the supported devices +static VendorModelEntry supportedDeviceList[] = +{ + {FW_VENDORID_RME, 0x0001, "RME", "Fireface-800"}, // RME Fireface-800 +}; + +RmeDevice::RmeDevice( Ieee1394Service& ieee1394Service, + std::auto_ptr( configRom )) + : FFADODevice( ieee1394Service, configRom ) + , m_model( NULL ) +{ + 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 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 * +RmeDevice::createDevice( Ieee1394Service& ieee1394Service, + std::auto_ptr( configRom )) +{ + return new RmeDevice(ieee1394Service, configRom ); +} + +bool +RmeDevice::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 +RmeDevice::getSamplingFrequency( ) { +/* + * Retrieve the current sample rate from the RME device. + */ + return 48000; +} + +int +RmeDevice::getConfigurationId() +{ + return 0; +} + +bool +RmeDevice::setSamplingFrequency( int samplingFrequency ) +{ +/* + * Set the RME device's samplerate. + */ + if (samplingFrequency == 48000) + return true; + return false; +} + +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; + +} + +} Index: /branches/api-cleanup/src/motu/motu_avdevice.h =================================================================== --- /branches/api-cleanup/src/motu/motu_avdevice.h (revision 750) +++ /branches/api-cleanup/src/motu/motu_avdevice.h (revision 750) @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2005-2007 by Pieter Palmers + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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" + +#define MOTUFW_BASE_ADDR 0xfffff0000000ULL + +#define MOTUFW_RATE_BASE_44100 (0<<3) +#define MOTUFW_RATE_BASE_48000 (1<<3) +#define MOTUFW_RATE_MULTIPLIER_1X (0<<4) +#define MOTUFW_RATE_MULTIPLIER_2X (1<<4) +#define MOTUFW_RATE_MULTIPLIER_4X (2<<4) +#define MOTUFW_RATE_BASE_MASK (0x00000008) +#define MOTUFW_RATE_MULTIPLIER_MASK (0x00000030) + +#define MOTUFW_OPTICAL_MODE_OFF 0x00 +#define MOTUFW_OPTICAL_MODE_ADAT 0x01 +#define MOTUFW_OPTICAL_MODE_TOSLINK 0x02 +#define MOTUFW_OPTICAL_IN_MODE_MASK (0x00000300) +#define MOTUFW_OPTICAL_OUT_MODE_MASK (0x00000c00) +#define MOTUFW_OPTICAL_MODE_MASK (MOTUFW_OPTICAL_IN_MODE_MASK|MOTUFW_OPTICAL_MODE_MASK) + +#define MOTUFW_CLKSRC_MASK 0x00000007 +#define MOTUFW_CLKSRC_INTERNAL 0 +#define MOTUFW_CLKSRC_ADAT_OPTICAL 1 +#define MOTUFW_CLKSRC_SPDIF_TOSLINK 2 +#define MOTUFW_CLKSRC_SMTPE 3 +#define MOTUFW_CLKSRC_WORDCLOCK 4 +#define MOTUFW_CLKSRC_ADAT_9PIN 5 +#define MOTUFW_CLKSRC_AES_EBU 7 + +#define MOTUFW_DIR_IN 1 +#define MOTUFW_DIR_OUT 2 +#define MOTUFW_DIR_INOUT (MOTUFW_DIR_IN | MOTUFW_DIR_OUT) + +/* Device registers */ +#define MOTUFW_REG_ISOCTRL 0x0b00 +#define MOTUFW_REG_OPTICAL_CTRL 0x0b10 +#define MOTUFW_REG_CLK_CTRL 0x0b14 +#define MOTUFW_REG_ROUTE_PORT_CONF 0x0c04 +#define MOTUFW_REG_CLKSRC_NAME0 0x0c60 + +/* Port Active Flags (ports declaration) */ +#define MOTUFW_PA_RATE_1x 0x0001 /* 44k1 or 48k */ +#define MOTUFW_PA_RATE_2x 0x0002 /* 88k2 or 96k */ +#define MOTUFW_PA_RATE_4x 0x0004 /* 176k4 or 192k */ +#define MOTUFW_PA_RATE_1x2x (MOTUFW_PA_RATE_1x|MOTUFW_PA_RATE_2x) +#define MOTUFW_PA_RATE_ANY (MOTUFW_PA_RATE_1x|MOTUFW_PA_RATE_2x|MOTUFW_PA_RATE_4x) +#define MOTUFW_PA_RATE_MASK MOTUFW_PA_RATE_ANY +#define MOTUFW_PA_OPTICAL_OFF 0x0010 /* Optical port off */ +#define MOTUFW_PA_OPTICAL_ADAT 0x0020 /* Optical port in ADAT mode */ +#define MOTUFW_PA_OPTICAL_TOSLINK 0x0040 /* Optical port in SPDIF/Toslink mode */ +#define MOTUFW_PA_OPTICAL_ON (MOTUFW_PA_OPTICAL_ADAT|MOTUFW_PA_OPTICAL_TOSLINK) +#define MOTUFW_PA_OPTICAL_ANY (MOTUFW_PA_OPTICAL_OFF|MOTUFW_PA_OPTICAL_ON) +#define MOTUFW_PA_OPTICAL_MASK MOTUFW_PA_OPTICAL_ANY + +class ConfigRom; +class Ieee1394Service; + +namespace Motu { + +enum EMotuModel { + MOTUFW_MODEL_NONE = 0x0000, + MOTUFW_MODEL_828mkII = 0x0001, + MOTUFW_MODEL_TRAVELER = 0x0002, + MOTUFW_MODEL_ULTRALITE= 0x0003, + MOTUFW_MODEL_8PRE = 0x0004, +}; + +struct VendorModelEntry { + unsigned int vendor_id; + unsigned int model_id; + unsigned int unit_version; + unsigned int unit_specifier_id; + enum EMotuModel model; + char *vendor_name; + char *model_name; +}; + +struct PortEntry { + char *port_name; + unsigned int port_dir; + unsigned int port_flags; + unsigned int port_offset; +}; + +struct DevicePropertyEntry { + const PortEntry* PortsList; + int PortsListLength; + int MaxSampleRate; + // Others features can be added here like MIDI port presence. +}; + +class MotuDevice : public FFADODevice { +public: + + MotuDevice( DeviceManager& d, std::auto_ptr( configRom ) ); + virtual ~MotuDevice(); + + 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); + + 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); + +protected: + signed int m_motu_model; + 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); + + unsigned int ReadRegister(unsigned int reg); + signed int WriteRegister(unsigned int reg, quadlet_t data); + +}; + +} + +#endif Index: /branches/api-cleanup/src/motu/motu_avdevice.cpp =================================================================== --- /branches/api-cleanup/src/motu/motu_avdevice.cpp (revision 785) +++ /branches/api-cleanup/src/motu/motu_avdevice.cpp (revision 785) @@ -0,0 +1,969 @@ +/* + * Copyright (C) 2005-2007 by Pieter Palmers + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "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 +#include +#include +#include +#include +#include + +#include + +namespace Motu { + +// to define the supported devices +static VendorModelEntry supportedDeviceList[] = +{ +// {vendor_id, model_id, unit_version, unit_specifier_id, model, vendor_name,model_name} + {FW_VENDORID_MOTU, 0, 0x00000003, 0x000001f2, MOTUFW_MODEL_828mkII, "MOTU", "828MkII"}, + {FW_VENDORID_MOTU, 0, 0x00000009, 0x000001f2, MOTUFW_MODEL_TRAVELER, "MOTU", "Traveler"}, + {FW_VENDORID_MOTU, 0, 0x0000000d, 0x000001f2, MOTUFW_MODEL_ULTRALITE, "MOTU", "UltraLite"}, + {FW_VENDORID_MOTU, 0, 0x0000000f, 0x000001f2, MOTUFW_MODEL_8PRE, "MOTU", "8pre"}, +}; + +// Ports declarations +const PortEntry Ports_828MKII[] = +{ + {"Main-L", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 40}, + {"Main-R", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 43}, + {"Mix-L", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 10}, + {"Mix-R", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 13}, + {"Analog1", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 16}, + {"Analog2", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 19}, + {"Analog3", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 22}, + {"Analog4", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 25}, + {"Analog5", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 28}, + {"Analog6", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 31}, + {"Analog7", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 34}, + {"Analog8", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 37}, + {"Phones-L", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 10}, + {"Phones-R", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 13}, + {"Mic1", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 40}, + {"Mic2", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 43}, + {"SPDIF1", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 46}, + {"SPDIF2", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 49}, + {"ADAT1", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 52}, + {"ADAT2", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 55}, + {"ADAT3", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 58}, + {"ADAT4", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 61}, + {"ADAT5", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x|MOTUFW_PA_OPTICAL_ADAT, 63}, + {"ADAT6", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x|MOTUFW_PA_OPTICAL_ADAT, 66}, + {"ADAT7", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x|MOTUFW_PA_OPTICAL_ADAT, 69}, + {"ADAT8", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x|MOTUFW_PA_OPTICAL_ADAT, 72}, +}; + +const PortEntry Ports_TRAVELER[] = +{ + {"Mix-L", MOTUFW_DIR_IN, MOTUFW_PA_RATE_1x2x|MOTUFW_PA_OPTICAL_ANY, 10}, + {"Mix-R", MOTUFW_DIR_IN, MOTUFW_PA_RATE_1x2x|MOTUFW_PA_OPTICAL_ANY, 13}, + {"Phones-L", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_1x2x|MOTUFW_PA_OPTICAL_ANY, 10}, + {"Phones-R", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_1x2x|MOTUFW_PA_OPTICAL_ANY, 13}, + {"Analog1", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 16}, + {"Analog2", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 19}, + {"Analog3", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 22}, + {"Analog4", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 25}, + {"Analog5", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 28}, + {"Analog6", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 31}, + {"Analog7", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 34}, + {"Analog8", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 37}, + {"AES/EBU1", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x2x|MOTUFW_PA_OPTICAL_ANY, 40}, + {"AES/EBU2", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x2x|MOTUFW_PA_OPTICAL_ANY, 43}, + {"SPDIF1", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x2x|MOTUFW_PA_OPTICAL_OFF|MOTUFW_PA_OPTICAL_ADAT, 46}, + {"SPDIF2", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x2x|MOTUFW_PA_OPTICAL_OFF|MOTUFW_PA_OPTICAL_ADAT, 49}, + {"Toslink1", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x2x|MOTUFW_PA_OPTICAL_TOSLINK, 46}, + {"Toslink2", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x2x|MOTUFW_PA_OPTICAL_TOSLINK, 49}, + {"ADAT1", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x2x|MOTUFW_PA_OPTICAL_ADAT, 52}, + {"ADAT2", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x2x|MOTUFW_PA_OPTICAL_ADAT, 55}, + {"ADAT3", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x2x|MOTUFW_PA_OPTICAL_ADAT, 58}, + {"ADAT4", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x2x|MOTUFW_PA_OPTICAL_ADAT, 61}, + {"ADAT5", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x|MOTUFW_PA_OPTICAL_ADAT, 63}, + {"ADAT6", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x|MOTUFW_PA_OPTICAL_ADAT, 66}, + {"ADAT7", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x|MOTUFW_PA_OPTICAL_ADAT, 69}, + {"ADAT8", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_1x|MOTUFW_PA_OPTICAL_ADAT, 72}, +}; + +const PortEntry Ports_ULTRALITE[] = +{ + {"Main-L", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 40}, + {"Main-R", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 43}, + {"Mix-L", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 10}, + {"Mix-R", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 13}, + {"Mic1", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 16}, + {"Mic2", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 19}, + {"Analog1", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 16}, + {"Analog2", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 19}, + {"Analog3", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 22}, + {"Analog4", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 25}, + {"Analog5", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 28}, + {"Analog6", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 31}, + {"Analog7", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 34}, + {"Analog8", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 37}, + {"Phones-L", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 10}, + {"Phones-R", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 13}, + {"SPDIF1", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 40}, + {"SPDIF2", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 43}, + {"SPDIF1", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 46}, + {"SPDIF2", MOTUFW_DIR_INOUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 49}, +}; + +const PortEntry Ports_8PRE[] = +{ + {"Analog1", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 16}, + {"Analog2", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 19}, + {"Analog3", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 22}, + {"Analog4", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 25}, + {"Analog5", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 28}, + {"Analog6", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 31}, + {"Analog7", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 34}, + {"Analog8", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 37}, + {"Mix-L", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 10}, + {"Mix-R", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 13}, + {"Main-L", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 16}, + {"Main-R", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 19}, + {"Phones-L", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 10}, + {"Phones-R", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ANY, 13}, + {"ADAT1", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 40}, + {"ADAT1", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 22}, + {"ADAT2", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 43}, + {"ADAT2", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 25}, + {"ADAT3", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 46}, + {"ADAT3", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 28}, + {"ADAT4", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 49}, + {"ADAT4", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 31}, + {"ADAT5", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 52}, + {"ADAT5", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 34}, + {"ADAT6", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 55}, + {"ADAT6", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 37}, + {"ADAT7", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 58}, + {"ADAT7", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 40}, + {"ADAT8", MOTUFW_DIR_IN, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 61}, + {"ADAT8", MOTUFW_DIR_OUT, MOTUFW_PA_RATE_ANY|MOTUFW_PA_OPTICAL_ADAT, 43}, +}; + +const DevicePropertyEntry DevicesProperty[] = { +// { Ports_map, sizeof( Ports_map ), MaxSR }, + { Ports_828MKII, sizeof( Ports_828MKII ), 96000 }, + { Ports_TRAVELER, sizeof( Ports_TRAVELER ), 192000 }, + { Ports_ULTRALITE, sizeof( Ports_ULTRALITE ), 96000 }, + { Ports_8PRE, sizeof( Ports_8PRE ), 96000 }, +}; + +MotuDevice::MotuDevice( DeviceManager& d, std::auto_ptr( configRom )) + : FFADODevice( d, configRom ) + , m_motu_model( MOTUFW_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 ) + +{ + 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); + } +} + +bool +MotuDevice::probe( ConfigRom& configRom ) +{ + unsigned int vendorId = configRom.getNodeVendorId(); +// unsigned int modelId = configRom.getModelId(); + 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].model_id == modelId ) + && ( 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 modelId = getConfigRom().getModelId(); + 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].model_id == modelId ) + && ( 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) { + debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n", + m_model->vendor_name, m_model->model_name); + return true; + } + + return false; +} + +int +MotuDevice::getSamplingFrequency( ) { +/* + * Retrieve the current sample rate from the MOTU device. + */ + quadlet_t q = ReadRegister(MOTUFW_REG_CLK_CTRL); + int rate = 0; + + switch (q & MOTUFW_RATE_BASE_MASK) { + case MOTUFW_RATE_BASE_44100: + rate = 44100; + break; + case MOTUFW_RATE_BASE_48000: + rate = 48000; + break; + } + switch (q & MOTUFW_RATE_MULTIPLIER_MASK) { + case MOTUFW_RATE_MULTIPLIER_2X: + rate *= 2; + break; + case MOTUFW_RATE_MULTIPLIER_4X: + rate *= 4; + break; + } + return rate; +} + +int +MotuDevice::getConfigurationId() +{ + return 0; +} + +bool +MotuDevice::setSamplingFrequency( int samplingFrequency ) +{ +/* + * Set the MOTU device's samplerate. + */ + char *src_name; + quadlet_t q, new_rate=0; + int i, supported=true, cancel_adat=false; + + if ( samplingFrequency > DevicesProperty[m_motu_model-1].MaxSampleRate ) + return false; + + switch ( samplingFrequency ) { + case 22050: + case 24000: + case 32000: + supported=false; + break; + case 44100: + new_rate = MOTUFW_RATE_BASE_44100 | MOTUFW_RATE_MULTIPLIER_1X; + break; + case 48000: + new_rate = MOTUFW_RATE_BASE_48000 | MOTUFW_RATE_MULTIPLIER_1X; + break; + case 88200: + new_rate = MOTUFW_RATE_BASE_44100 | MOTUFW_RATE_MULTIPLIER_2X; + break; + case 96000: + new_rate = MOTUFW_RATE_BASE_48000 | MOTUFW_RATE_MULTIPLIER_2X; + break; + case 176400: + new_rate = MOTUFW_RATE_BASE_44100 | MOTUFW_RATE_MULTIPLIER_4X; + cancel_adat = true; // current ADAT protocol doesn't support sample rate > 96000 + break; + case 192000: + new_rate = MOTUFW_RATE_BASE_48000 | MOTUFW_RATE_MULTIPLIER_4X; + cancel_adat = true; + break; + default: + supported=false; + } + + // Update the clock control register. FIXME: while this is now rather + // comprehensive there may still be a need to manipulate MOTUFW_REG_CLK_CTRL + // a little more than we do. + if (supported) { + quadlet_t value=ReadRegister(MOTUFW_REG_CLK_CTRL); + + // 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(MOTUFW_DIR_INOUT, MOTUFW_OPTICAL_MODE_OFF); + } + + value &= ~(MOTUFW_RATE_BASE_MASK|MOTUFW_RATE_MULTIPLIER_MASK); + value |= new_rate; + + // In other OSes bit 26 of MOTUFW_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 MOTUFW_REG_CLK_CTRL multiple times, so that may be + // part of the mystery. + // value |= 0x04000000; + if (WriteRegister(MOTUFW_REG_CLK_CTRL, value) == 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 (value & MOTUFW_CLKSRC_MASK) { + case MOTUFW_CLKSRC_INTERNAL: + src_name = "Internal "; + break; + case MOTUFW_CLKSRC_ADAT_OPTICAL: + src_name = "ADAT Optical "; + break; + case MOTUFW_CLKSRC_SPDIF_TOSLINK: + if (getOpticalMode(MOTUFW_DIR_IN) == MOTUFW_OPTICAL_MODE_TOSLINK) + src_name = "TOSLink "; + else + src_name = "SPDIF "; + break; + case MOTUFW_CLKSRC_SMTPE: + src_name = "SMPTE "; + break; + case MOTUFW_CLKSRC_WORDCLOCK: + src_name = "Word Clock In "; + break; + case MOTUFW_CLKSRC_ADAT_9PIN: + src_name = "ADAT 9-pin "; + break; + case MOTUFW_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(MOTUFW_REG_CLKSRC_NAME0+i, q); + } + } + return supported; +} + +FFADODevice::ClockSourceVector +MotuDevice::getSupportedClockSources() { + FFADODevice::ClockSourceVector r; + return r; +} + +bool +MotuDevice::setActiveClockSource(ClockSource s) { + return false; +} + +FFADODevice::ClockSource +MotuDevice::getActiveClockSource() { + ClockSource s; + 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(MOTUFW_DIR_IN); + unsigned int optical_out_mode = getOpticalMode(MOTUFW_DIR_OUT); + unsigned int event_size_in = getEventSize(MOTUFW_DIR_IN); + unsigned int event_size_out= getEventSize(MOTUFW_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(buff, + Streaming::Port::E_Capture, 4); + 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"); + free(buff); + return false; + } else { + debugOutput(DEBUG_LEVEL_VERBOSE, "Added 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(buff, + Streaming::Port::E_Capture, 4); + 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"); + free(buff); + return false; + } else { + debugOutput(DEBUG_LEVEL_VERBOSE, "Added 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(MOTUFW_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(MOTUFW_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(MOTUFW_REG_ISOCTRL, isoctrl); + break; + + default: // Invalid stream index + return false; + } + + return true; +} + +bool +MotuDevice::stopStreamByIndex(int i) { + +quadlet_t isoctrl = ReadRegister(MOTUFW_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(MOTUFW_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(MOTUFW_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(MOTUFW_REG_ROUTE_PORT_CONF); + +debugOutput(DEBUG_LEVEL_VERBOSE, "optical mode: %x %x %x %x\n",dir, reg, reg & MOTUFW_OPTICAL_IN_MODE_MASK, +reg & MOTUFW_OPTICAL_OUT_MODE_MASK); + + if (dir == MOTUFW_DIR_IN) + return (reg & MOTUFW_OPTICAL_IN_MODE_MASK) >> 8; + else + return (reg & MOTUFW_OPTICAL_OUT_MODE_MASK) >> 10; +} + +signed int MotuDevice::setOpticalMode(unsigned int dir, unsigned int mode) { + unsigned int reg = ReadRegister(MOTUFW_REG_ROUTE_PORT_CONF); + unsigned int opt_ctrl = 0x0000002; + + // 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 & MOTUFW_OPTICAL_IN_MODE_MASK != (MOTUFW_OPTICAL_MODE_ADAT<<8)) + opt_ctrl |= 0x00000080; + if (reg & MOTUFW_OPTICAL_OUT_MODE_MASK != (MOTUFW_OPTICAL_MODE_ADAT<<10)) + opt_ctrl |= 0x00000040; + + if (mode & MOTUFW_DIR_IN) { + reg &= ~MOTUFW_OPTICAL_IN_MODE_MASK; + reg |= (mode << 8) & MOTUFW_OPTICAL_IN_MODE_MASK; + if (mode != MOTUFW_OPTICAL_MODE_ADAT) + opt_ctrl |= 0x00000080; + else + opt_ctrl &= ~0x00000080; + } + if (mode & MOTUFW_DIR_OUT) { + reg &= ~MOTUFW_OPTICAL_OUT_MODE_MASK; + reg |= (mode <<10) & MOTUFW_OPTICAL_OUT_MODE_MASK; + if (mode != MOTUFW_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(MOTUFW_REG_ROUTE_PORT_CONF, reg); + return WriteRegister(MOTUFW_REG_OPTICAL_CTRL, opt_ctrl); +} + +signed int MotuDevice::getEventSize(unsigned int direction) { +// +// Return the size in bytes of a single event sent to (dir==MOTUFW_OUT) or +// from (dir==MOTUFW_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?MOTUFW_DIR_IN:MOTUFW_DIR_OUT; +unsigned int flags = (1 << ( optical_mode + 4 )); + + if ( sample_rate > 96000 ) + flags |= MOTUFW_PA_RATE_4x; + else if ( sample_rate > 48000 ) + flags |= MOTUFW_PA_RATE_2x; + else + flags |= MOTUFW_PA_RATE_1x; + + for (i=0; i < ( DevicesProperty[m_motu_model-1].PortsListLength /sizeof( PortEntry ) ); i++) { + if (( DevicesProperty[m_motu_model-1].PortsList[i].port_dir & dir ) && + ( DevicesProperty[m_motu_model-1].PortsList[i].port_flags & MOTUFW_PA_RATE_MASK & flags ) && + ( DevicesProperty[m_motu_model-1].PortsList[i].port_flags & MOTUFW_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(name, direction, position, size); + + if (!p) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Skipped port %s\n",name); + } else { + if (!s_processor->addPort(p)) { + debugWarning("Could not register port with stream processor\n"); + free(name); + return false; + } else { + debugOutput(DEBUG_LEVEL_VERBOSE, "Added port %s\n",name); + } + p->enable(); + } + 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?MOTUFW_DIR_IN:MOTUFW_DIR_OUT; +unsigned int flags = (1 << ( optical_mode + 4 )); + + if ( sample_rate > 96000 ) + flags |= MOTUFW_PA_RATE_4x; + else if ( sample_rate > 48000 ) + flags |= MOTUFW_PA_RATE_2x; + else + flags |= MOTUFW_PA_RATE_1x; + + // retrieve the ID + std::string id=std::string("dev?"); + if(!getOption("id", id)) { + debugWarning("Could not retrieve id parameter, defauling 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].PortsListLength /sizeof( PortEntry ) ); i++) { + if (( DevicesProperty[m_motu_model-1].PortsList[i].port_dir & dir ) && + ( DevicesProperty[m_motu_model-1].PortsList[i].port_flags & MOTUFW_PA_RATE_MASK & flags ) && + ( DevicesProperty[m_motu_model-1].PortsList[i].port_flags & MOTUFW_PA_OPTICAL_MASK & flags )) { + asprintf(&buff,"%s_%s_%s" , id.c_str(), mode_str, + DevicesProperty[m_motu_model-1].PortsList[i].port_name); + if (!addPort(s_processor, buff, direction, DevicesProperty[m_motu_model-1].PortsList[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(), MOTUFW_BASE_ADDR+reg, 1, &quadlet) < 0) { + debugError("Error doing motu read from register 0x%06x\n",reg); + } + + return ntohl(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 = htonl(data); + + // Note: 1394Service::write() expects a physical ID, not the node id + if (get1394Service().write(0xffc0 | getNodeId(), MOTUFW_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/api-cleanup/src/ffadodevice.h =================================================================== --- /branches/api-cleanup/src/ffadodevice.h (revision 750) +++ /branches/api-cleanup/src/ffadodevice.h (revision 750) @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * Copyright (C) 2005-2007 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 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 . + * + */ + +#ifndef FFADODEVICE_H +#define FFADODEVICE_H + +#include "libutil/OptionContainer.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; +} + +/*! +@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_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 + }; + + /** + * @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 + */ + struct sClockSource { + sClockSource() + : 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; + }; + typedef struct sClockSource ClockSource; + + 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 handle a bus reset + * + * Called whenever a bus reset is detected. Handle everything + * that has to be done to cope with a bus reset. + * + */ + 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; +protected: + DECLARE_DEBUG_MODULE; +}; + +#endif Index: /branches/api-cleanup/src/SConscript =================================================================== --- /branches/api-cleanup/src/SConscript (revision 790) +++ /branches/api-cleanup/src/SConscript (revision 790) @@ -0,0 +1,276 @@ +# +# 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( 'env' ) + +env.AppendUnique( CPPPATH=["#/", "#/src"] ) + +libenv = env.Copy() + +ffado_source = env.Split( '\ + devicemanager.cpp \ + ffado.cpp \ + ffadodevice.cpp \ + debugmodule/debugmodule.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/PacketBuffer.cpp \ + libutil/OptionContainer.cpp \ + libutil/PosixThread.cpp \ + libutil/ringbuffer.c \ + libutil/serialize.cpp \ + libutil/StreamStatistics.cpp \ + libutil/SystemTimeSource.cpp \ + libutil/TimeSource.cpp \ + libutil/TimestampedBuffer.cpp \ + libcontrol/Element.cpp \ + libcontrol/BasicElements.cpp \ + libcontrol/MatrixMixer.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 \ + 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 \ + 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'] ) + libenv.MergeFlags( env['ALSA_FLAGS'] ) + libenv.MergeFlags( env['LIBXML26_FLAGS'] ) + +#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.Copy() +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/api-cleanup/src/metrichalo/mh_avdevice.h =================================================================== --- /branches/api-cleanup/src/metrichalo/mh_avdevice.h (revision 742) +++ /branches/api-cleanup/src/metrichalo/mh_avdevice.h (revision 742) @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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/api-cleanup/src/metrichalo/mh_avdevice.cpp =================================================================== --- /branches/api-cleanup/src/metrichalo/mh_avdevice.cpp (revision 742) +++ /branches/api-cleanup/src/metrichalo/mh_avdevice.cpp (revision 742) @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +#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 +#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/api-cleanup/src/debugmodule/debugmodule.h =================================================================== --- /branches/api-cleanup/src/debugmodule/debugmodule.h (revision 790) +++ /branches/api-cleanup/src/debugmodule/debugmodule.h (revision 790) @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * Copyright (C) 2005-2007 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 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 . + * + */ + +#ifndef DEBUGMODULE_H +#define DEBUGMODULE_H + +#include "../fbtypes.h" +#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 + +#define DEBUG_MAX_MESSAGE_LENGTH 512 + +/* MB_NEXT() relies on the fact that MB_BUFFERS is a power of two */ +#define MB_BUFFERS (1<<16) + +#define MB_NEXT(index) (((index)+1) & (MB_BUFFERS-1)) + +#define MB_BUFFERSIZE DEBUG_MAX_MESSAGE_LENGTH + +#ifdef DEBUG + #define IMPLEMENT_BACKLOG +#endif + +#ifdef IMPLEMENT_BACKLOG +// the backlog is a similar buffer as the message buffer +#define BACKLOG_MB_BUFFERS (256) +#define BACKLOG_MB_NEXT(index) (((index)+1) & (BACKLOG_MB_BUFFERS-1)) +#define BACKLOG_MIN_LEVEL DEBUG_LEVEL_VERY_VERBOSE +#endif + +#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 +#define printMessage( format, args... ) \ + m_debugModule.print( DebugModule::eDL_Message, \ + __FILE__, \ + __FUNCTION__, \ + __LINE__, \ + format, \ + ##args ) +#define printMessageShort( format, args... ) \ + m_debugModule.printShort( DebugModule::eDL_Message, \ + format, \ + ##args ) + +#define DECLARE_DEBUG_MODULE static 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() + +#ifdef IMPLEMENT_BACKLOG + +#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, \ + __FILE__, \ + __FUNCTION__, \ + __LINE__, \ + "Backlog print requested\n"); \ + DebugModuleManager::instance()->showBackLog(x); \ + } + +#else +#define debugShowBackLog() +#define debugShowBackLogLines(x) + +#endif + +#ifdef DEBUG + + #define debugOutput( level, format, args... ) \ + m_debugModule.print( level, \ + __FILE__, \ + __FUNCTION__, \ + __LINE__, \ + format, \ + ##args ) + + #define debugOutputShort( level, format, args... ) \ + m_debugModule.printShort( level, \ + format, \ + ##args ) + +#else + + #define debugOutput( level, format, args... ) + #define debugOutputShort( level, format, args... ) + +#endif + +/* Enable preemption checking for Linux Realtime Preemption kernels. + * + * This checks if any RT-safe code section does anything to cause CPU + * preemption. Examples are sleep() or other system calls that block. + * If a problem is detected, the kernel writes a syslog entry, and + * sends SIGUSR2 to the client. + */ + +// #define DO_PREEMPTION_CHECKING + +#include + +#ifdef DO_PREEMPTION_CHECKING +#define CHECK_PREEMPTION(onoff) \ + gettimeofday((struct timeval *)1, (struct timezone *)onoff) +#else +#define CHECK_PREEMPTION(onoff) +#endif + +unsigned char toAscii( unsigned char c ); +void quadlet2char( fb_quadlet_t quadlet, unsigned char* buff ); +void hexDump( unsigned char *data_start, unsigned int length ); +void hexDumpQuadlets( quadlet_t *data_start, unsigned int length ); + +class DebugModule { +public: + enum { + eDL_Message = DEBUG_LEVEL_MESSAGE, + eDL_Fatal = DEBUG_LEVEL_FATAL, + eDL_Error = DEBUG_LEVEL_ERROR, + eDL_Warning = DEBUG_LEVEL_WARNING, + eDL_Normal = DEBUG_LEVEL_NORMAL, + eDL_Info = DEBUG_LEVEL_INFO, + eDL_Verbose = DEBUG_LEVEL_VERBOSE, + eDL_VeryVerbose = DEBUG_LEVEL_VERY_VERBOSE, + eDL_UltraVerbose = DEBUG_LEVEL_ULTRA_VERBOSE, + } EDebugLevel; + + DebugModule( std::string name, debug_level_t level ); + virtual ~DebugModule(); + + void printShort( debug_level_t level, + const char* format, + ... ) const; + + void print( debug_level_t level, + const char* file, + const char* function, + unsigned int line, + const char* format, + ... ) const; + + bool setLevel( debug_level_t level ) + { m_level = level; return true; } + debug_level_t getLevel() + { return m_level; } + std::string getName() + { return m_name; } + +protected: + const char* getPreSequence( debug_level_t level ) const; + const char* getPostSequence( debug_level_t level ) const; + +private: + std::string m_name; + debug_level_t m_level; +}; + + +class DebugModuleManager { +public: + friend class DebugModule; + + static DebugModuleManager* instance(); + ~DebugModuleManager(); + + bool setMgrDebugLevel( std::string name, debug_level_t level ); + + void flush(); + + // the backlog is a ringbuffer of all the messages + // that have been recorded using the debugPrint + // statements, regardless of the debug level. + // This is useful to obtain more debug info + // when something goes wrong without having too + // much output in normal operation + void showBackLog(); + void showBackLog(int nblines); + +protected: + bool registerModule( DebugModule& debugModule ); + bool unregisterModule( DebugModule& debugModule ); + + bool init(); + + void print(const char *msg); + void backlog_print(const char *msg); + +private: + DebugModuleManager(); + + typedef std::vector< DebugModule* > DebugModuleVector; + typedef std::vector< DebugModule* >::iterator DebugModuleVectorIterator; + + char mb_buffers[MB_BUFFERS][MB_BUFFERSIZE]; + unsigned int mb_initialized; + unsigned int mb_inbuffer; + unsigned int mb_outbuffer; + unsigned int mb_overruns; + pthread_t mb_writer_thread; + pthread_mutex_t mb_write_lock; + pthread_mutex_t mb_flush_lock; + pthread_cond_t mb_ready_cond; + + static void *mb_thread_func(void *arg); + void mb_flush(); + +#ifdef IMPLEMENT_BACKLOG + // the backlog + char bl_mb_buffers[BACKLOG_MB_BUFFERS][MB_BUFFERSIZE]; + unsigned int bl_mb_inbuffer; + pthread_mutex_t bl_mb_write_lock; +#endif + + static DebugModuleManager* m_instance; + DebugModuleVector m_debugModules; +}; + +#endif Index: /branches/api-cleanup/src/debugmodule/test_debugmodule.cpp =================================================================== --- /branches/api-cleanup/src/debugmodule/test_debugmodule.cpp (revision 742) +++ /branches/api-cleanup/src/debugmodule/test_debugmodule.cpp (revision 742) @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "debugmodule.h" + +#include + +using namespace std; + +class Test +{ +public: + Test() + {} + ~Test() + {} + + bool run() { + cout << "######################" << endl; + cout << "### Test arguments ###" << endl; + cout << "######################" << endl; + debugOutput( DEBUG_LEVEL_NORMAL, "arg0 = %d, arg1 = 0x%08x\n" + , 1, 0xdeedbeef ); + cout << endl << endl; + + + cout << "###################" << endl; + cout << "### Test levels ###" << endl; + cout << "###################" << endl; + for ( debug_level_t level = DEBUG_LEVEL_VERBOSE; + level >= 0; + --level ) + { + DebugModuleManager::instance()->setMgrDebugLevel( "Test", level ); + + cout << endl << "*** Debug Level = " << level << endl << endl; + + debugFatal( "fatal text\n" ); + debugError( "error text\n" ); + debugWarning( "warning text\n" ); + debugOutput( DEBUG_LEVEL_NORMAL, "normal output\n" ); + debugOutput( DEBUG_LEVEL_VERBOSE, "verbose output\n" ); + debugFatalShort( "fatal short text\n" ); + debugErrorShort( "error short text\n" ); + debugWarningShort( "warning short text\n" ); + debugOutputShort( DEBUG_LEVEL_NORMAL, "normal short output\n" ); + debugOutputShort( DEBUG_LEVEL_VERBOSE, "verbose short output\n" ); + } + cout << endl << endl; + + return true; + } + + DECLARE_DEBUG_MODULE; +}; + +IMPL_DEBUG_MODULE( Test, Test, DEBUG_LEVEL_VERBOSE ); + +DECLARE_GLOBAL_DEBUG_MODULE; +IMPL_GLOBAL_DEBUG_MODULE( Test, DEBUG_LEVEL_VERBOSE ); + +int main( int argc, char** argv ) +{ + cout << "#################################" << endl; + cout << "### Test global debug module ###" << endl; + cout << "#################################" << endl; + debugOutput( DEBUG_LEVEL_NORMAL, "foobar\n" ); + cout << endl << endl; + + Test test; + test.run(); + + return 0; +} + +/* + * Local variables: + * compile-command: "g++ -Wall -g -DDEBUG -o test test.cpp -L. -ldebugmodule" + * End: + */ + Index: /branches/api-cleanup/src/debugmodule/debugmodule.cpp =================================================================== --- /branches/api-cleanup/src/debugmodule/debugmodule.cpp (revision 742) +++ /branches/api-cleanup/src/debugmodule/debugmodule.cpp (revision 742) @@ -0,0 +1,656 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "debugmodule.h" + +#include +#include + +#include + +#include + +#define DO_MESSAGE_BUFFER_PRINT + +#ifndef DO_MESSAGE_BUFFER_PRINT + #warning Printing debug info without ringbuffer, not RT-safe! +#endif + +using namespace std; + +struct ColorEntry { + const char* preSequence; + const char* postSequence; +}; + +ColorEntry colorTable[] = { + { "", "" }, + { "\033[31mFatal", "\033[0m" }, + { "\033[31mError", "\033[0m" }, + { "\033[31mWarning", "\033[0m" }, + { "Debug", "" }, +}; + + +DebugModule::DebugModule( std::string name, debug_level_t level ) + : m_name( name ) + , m_level( level ) +{ + if ( !DebugModuleManager::instance()->registerModule( *this ) ) { + cerr << "Could not register DebugModule (" << name + << ") at DebugModuleManager" + << endl; + } +} + +DebugModule::~DebugModule() +{ +// if ( m_level >= eDL_VeryVerbose ) { +// cout << "Unregistering " +// << this->getName() +// << " at DebugModuleManager" +// << endl; +// } + if ( !DebugModuleManager::instance()->unregisterModule( *this ) ) { + cerr << "Could not unregister DebugModule at DebugModuleManager" + << endl; + } + +} + +void +DebugModule::printShort( debug_level_t level, + const char* format, + ... ) const +{ + + // bypass for performance +#ifdef IMPLEMENT_BACKLOG + if (level > BACKLOG_MIN_LEVEL + && level > m_level) { + return; + } +#else + if ( level >m_level ) { + return; + } +#endif + + const char *warning = "WARNING: message truncated!\n"; + const int warning_size = 32; + va_list arg; + char msg[MB_BUFFERSIZE]; + + // format the message such that it remains together + int chars_written=0; + int retval=0; + + va_start( arg, format ); + retval = vsnprintf(msg+chars_written, MB_BUFFERSIZE, format, arg); + va_end( arg ); + if (retval >= 0) { // ignore errors + chars_written += retval; + } + + // output a warning if the message was truncated + if (chars_written == MB_BUFFERSIZE) { + snprintf(msg+MB_BUFFERSIZE-warning_size, warning_size, "%s", warning); + } + +#ifdef IMPLEMENT_BACKLOG + // print to backlog if necessary + if (level <= BACKLOG_MIN_LEVEL) { + DebugModuleManager::instance()->backlog_print( msg ); + } +#endif + + // print to stderr if necessary + if ( level <= m_level ) { + DebugModuleManager::instance()->print( msg ); + } +} + +void +DebugModule::print( debug_level_t level, + const char* file, + const char* function, + unsigned int line, + const char* format, + ... ) const +{ + // bypass for performance +#ifdef IMPLEMENT_BACKLOG + if (level > BACKLOG_MIN_LEVEL + && level > m_level) { + return; + } +#else + if ( level >m_level ) { + return; + } +#endif + + const char *warning = "WARNING: message truncated!\n"; + const int warning_size = 32; + + va_list arg; + char msg[MB_BUFFERSIZE]; + + // remove the path info from the filename + const char *f = file; + const char *fname = file; + while((f=strstr(f, "/"))) { + f++; // move away from delimiter + fname=f; + } + + // add a timing timestamp + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + long unsigned int ts_usec=(uint32_t)(ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL); + + // format the message such that it remains together + int chars_written=0; + int retval=0; + retval = snprintf(msg, MB_BUFFERSIZE, "%010lu: %s (%s)[%4d] %s: ", + ts_usec, getPreSequence( level ), + fname, line, function ); + if (retval >= 0) chars_written += retval; // ignore errors + + va_start( arg, format ); + retval = vsnprintf( msg + chars_written, + MB_BUFFERSIZE - chars_written, + format, arg); + va_end( arg ); + if (retval >= 0) chars_written += retval; // ignore errors + + retval = snprintf( msg + chars_written, + MB_BUFFERSIZE - chars_written, + "%s", getPostSequence( level ) ); + if (retval >= 0) chars_written += retval; // ignore errors + + // output a warning if the message was truncated + if (chars_written == MB_BUFFERSIZE) { + snprintf(msg + MB_BUFFERSIZE - warning_size, + warning_size, + "%s", warning); + } + +#ifdef IMPLEMENT_BACKLOG + // print to backlog if necessary + if (level <= BACKLOG_MIN_LEVEL) { + DebugModuleManager::instance()->backlog_print( msg ); + } +#endif + + // print to stderr if necessary + if ( level <= m_level ) { + DebugModuleManager::instance()->print( msg ); + } +} + +const char* +DebugModule::getPreSequence( debug_level_t level ) const +{ + if ( ( level <= eDL_Normal ) && ( level >= eDL_Message ) ) { + return colorTable[level].preSequence; + } + return colorTable[eDL_Normal].preSequence; +} + +const char* +DebugModule::getPostSequence( debug_level_t level ) const +{ + if ( ( level <= eDL_Normal ) && ( level >= eDL_Message ) ) { + return colorTable[level].postSequence; + } + return colorTable[eDL_Normal].postSequence; +} + +//-------------------------------------- + +DebugModuleManager* DebugModuleManager::m_instance = 0; + +DebugModuleManager::DebugModuleManager() + : mb_initialized(0) + , mb_inbuffer(0) + , mb_outbuffer(0) + , mb_overruns(0) +#ifdef IMPLEMENT_BACKLOG + , bl_mb_inbuffer(0) +#endif +{ + +} + +DebugModuleManager::~DebugModuleManager() +{ + // cleanin up leftover modules + for ( DebugModuleVectorIterator it = m_debugModules.begin(); + it != m_debugModules.end(); + ++it ) + { + fprintf(stderr,"Cleaning up leftover debug module: %s\n",(*it)->getName().c_str()); + m_debugModules.erase( it ); + delete *it; + } + + if (!mb_initialized) + return; + + pthread_mutex_lock(&mb_write_lock); + mb_initialized = 0; + pthread_cond_signal(&mb_ready_cond); + pthread_mutex_unlock(&mb_write_lock); + + pthread_join(mb_writer_thread, NULL); + mb_flush(); + + if (mb_overruns) + fprintf(stderr, "WARNING: %d message buffer overruns!\n", + mb_overruns); + else + fprintf(stderr, "no message buffer overruns\n"); + + pthread_mutex_destroy(&mb_write_lock); + pthread_cond_destroy(&mb_ready_cond); + +#ifdef IMPLEMENT_BACKLOG + pthread_mutex_destroy(&bl_mb_write_lock); +#endif + +} + +bool +DebugModuleManager::init() +{ + if (mb_initialized) + return true; + + // if ( m_level >= eDL_VeryVerbose ) + // cout << "DebugModuleManager init..." << endl; + + pthread_mutex_init(&mb_write_lock, NULL); + pthread_mutex_init(&mb_flush_lock, NULL); + pthread_cond_init(&mb_ready_cond, NULL); + + mb_overruns = 0; + mb_initialized = 1; + +#ifdef IMPLEMENT_BACKLOG + pthread_mutex_init(&bl_mb_write_lock, NULL); +#endif + + if (pthread_create(&mb_writer_thread, NULL, &mb_thread_func, (void *)this) != 0) + mb_initialized = 0; + + return true; +} + +DebugModuleManager* +DebugModuleManager::instance() +{ + if ( !m_instance ) { + m_instance = new DebugModuleManager; + if ( !m_instance ) { + cerr << "DebugModuleManager::instance Failed to create " + << "DebugModuleManager" << endl; + } + if ( !m_instance->init() ) { + cerr << "DebugModuleManager::instance Failed to init " + << "DebugModuleManager" << endl; + } + } + return m_instance; +} + +bool +DebugModuleManager::registerModule( DebugModule& debugModule ) +{ + bool already_present=false; + + for ( DebugModuleVectorIterator it = m_debugModules.begin(); + it != m_debugModules.end(); + ++it ) + { + if ( *it == &debugModule ) { + already_present=true; + return true; + } + } + + if (already_present) { + cerr << "DebugModuleManager::registerModule: Module already registered: " + << "DebugModule (" << debugModule.getName() << ")" << endl; + } else { + m_debugModules.push_back( &debugModule ); + } + return true; +} + +bool +DebugModuleManager::unregisterModule( DebugModule& debugModule ) +{ + + for ( DebugModuleVectorIterator it = m_debugModules.begin(); + it != m_debugModules.end(); + ++it ) + { + if ( *it == &debugModule ) { + m_debugModules.erase( it ); + return true; + } + } + + cerr << "DebugModuleManager::unregisterModule: Could not unregister " + << "DebugModule (" << debugModule.getName() << ")" << endl; + return false; +} + +bool +DebugModuleManager::setMgrDebugLevel( std::string name, debug_level_t level ) +{ + for ( DebugModuleVectorIterator it = m_debugModules.begin(); + it != m_debugModules.end(); + ++it ) + { + if ( (*it)->getName() == name ) { + return (*it)->setLevel( level ); + } + } + + cerr << "setDebugLevel: Did not find DebugModule (" + << name << ")" << endl; + return false; +} + +void +DebugModuleManager::flush() +{ +#ifdef DO_MESSAGE_BUFFER_PRINT + mb_flush(); +#else + fflush(stderr); +#endif +} + +void +DebugModuleManager::mb_flush() +{ + /* called WITHOUT the mb_write_lock */ + + /* the flush lock is to allow a flush from multiple threads + * this allows a code section that outputs a lot of debug messages + * and that can be blocked to flush the buffer itself such that it + * does not overflow. + */ + DebugModuleManager *m=DebugModuleManager::instance(); + pthread_mutex_lock(&m->mb_flush_lock); + while (mb_outbuffer != mb_inbuffer) { + fputs(mb_buffers[mb_outbuffer], stderr); + mb_outbuffer = MB_NEXT(mb_outbuffer); + } + pthread_mutex_unlock(&m->mb_flush_lock); +} + +#ifdef IMPLEMENT_BACKLOG +void +DebugModuleManager::showBackLog() +{ + DebugModuleManager *m=DebugModuleManager::instance(); + // locking the flush lock ensures that the backlog is + // printed as one entity + pthread_mutex_lock(&m->mb_flush_lock); + fprintf(stderr, "=====================================================\n"); + fprintf(stderr, "* BEGIN OF BACKLOG PRINT\n"); + fprintf(stderr, "=====================================================\n"); + + for (unsigned int i=0; imb_flush_lock); +} + +void +DebugModuleManager::showBackLog(int nblines) +{ + DebugModuleManager *m=DebugModuleManager::instance(); + // locking the flush lock ensures that the backlog is + // printed as one entity + pthread_mutex_lock(&m->mb_flush_lock); + fprintf(stderr, "=====================================================\n"); + fprintf(stderr, "* BEGIN OF BACKLOG PRINT\n"); + fprintf(stderr, "=====================================================\n"); + + int lines_to_skip = BACKLOG_MB_BUFFERS - nblines; + if (lines_to_skip < 0) lines_to_skip = 0; + for (unsigned int i=0; imb_flush_lock); +} +#endif + +void * +DebugModuleManager::mb_thread_func(void *arg) +{ + + DebugModuleManager *m=static_cast(arg); + + /* The mutex is only to eliminate collisions between multiple + * writer threads and protect the condition variable. */ + pthread_mutex_lock(&m->mb_write_lock); + + while (m->mb_initialized) { + pthread_cond_wait(&m->mb_ready_cond, &m->mb_write_lock); + + /* releasing the mutex reduces contention */ + pthread_mutex_unlock(&m->mb_write_lock); + m->mb_flush(); + pthread_mutex_lock(&m->mb_write_lock); + } + + pthread_mutex_unlock(&m->mb_write_lock); + + return NULL; +} + +#ifdef IMPLEMENT_BACKLOG +void +DebugModuleManager::backlog_print(const char *msg) +{ + unsigned int ntries; + struct timespec wait = {0,50000}; + // the backlog + ntries=1; + while (ntries) { // try a few times + if (pthread_mutex_trylock(&bl_mb_write_lock) == 0) { + strncpy(bl_mb_buffers[bl_mb_inbuffer], msg, MB_BUFFERSIZE); + bl_mb_inbuffer = BACKLOG_MB_NEXT(bl_mb_inbuffer); + pthread_mutex_unlock(&bl_mb_write_lock); + break; + } else { + nanosleep(&wait, NULL); + ntries--; + } + } + // just bail out should it have failed +} +#endif + +void +DebugModuleManager::print(const char *msg) +{ +#ifdef DO_MESSAGE_BUFFER_PRINT + unsigned int ntries; + struct timespec wait = {0,50000}; +#endif + + if (!mb_initialized) { + /* Unable to print message with realtime safety. + * Complain and print it anyway. */ + fprintf(stderr, "ERROR: messagebuffer not initialized: %s", + msg); + return; + } + +#ifdef DO_MESSAGE_BUFFER_PRINT + ntries=1; + while (ntries) { // try a few times + if (pthread_mutex_trylock(&mb_write_lock) == 0) { + strncpy(mb_buffers[mb_inbuffer], msg, MB_BUFFERSIZE); + mb_inbuffer = MB_NEXT(mb_inbuffer); + pthread_cond_signal(&mb_ready_cond); + pthread_mutex_unlock(&mb_write_lock); + break; + } else { + nanosleep(&wait, NULL); + ntries--; + } + } + if (ntries==0) { /* lock collision */ + // atomic_add(&mb_overruns, 1); + // FIXME: atomicity + mb_overruns++; // skip the atomicness for now + } +#else + fprintf(stderr,msg); +#endif +} + +//---------------------------------------- + +unsigned char +toAscii( unsigned char c ) +{ + if ( ( c > 31 ) && ( c < 126) ) { + return c; + } else { + return '.'; + } +} + +/* converts a quadlet to a uchar * buffer + * not implemented optimally, but clear + */ +void +quadlet2char( quadlet_t quadlet, unsigned char* buff ) +{ + *(buff) = (quadlet>>24)&0xFF; + *(buff+1) = (quadlet>>16)&0xFF; + *(buff+2) = (quadlet>> 8)&0xFF; + *(buff+3) = (quadlet) &0xFF; +} + +void +hexDump( unsigned char *data_start, unsigned int length ) +{ + unsigned int i=0; + unsigned int byte_pos; + unsigned int bytes_left; + + if ( length <= 0 ) { + return; + } + if ( length >= 7 ) { + for ( i = 0; i < (length-7); i += 8 ) { + printf( "%04X: %02X %02X %02X %02X %02X %02X %02X %02X " + "- [%c%c%c%c%c%c%c%c]\n", + + i, + + *(data_start+i+0), + *(data_start+i+1), + *(data_start+i+2), + *(data_start+i+3), + *(data_start+i+4), + *(data_start+i+5), + *(data_start+i+6), + *(data_start+i+7), + + toAscii( *(data_start+i+0) ), + toAscii( *(data_start+i+1) ), + toAscii( *(data_start+i+2) ), + toAscii( *(data_start+i+3) ), + toAscii( *(data_start+i+4) ), + toAscii( *(data_start+i+5) ), + toAscii( *(data_start+i+6) ), + toAscii( *(data_start+i+7) ) + ); + } + } + byte_pos = i; + bytes_left = length - byte_pos; + + printf( "%04X:" ,i ); + for ( i = byte_pos; i < length; i += 1 ) { + printf( " %02X", *(data_start+i) ); + } + for ( i=0; i < 8-bytes_left; i+=1 ) { + printf( " " ); + } + + printf( " - [" ); + for ( i = byte_pos; i < length; i += 1) { + printf( "%c", toAscii(*(data_start+i))); + } + for ( i = 0; i < 8-bytes_left; i += 1) { + printf( " " ); + } + + printf( "]" ); + printf( "\n" ); +} + +void +hexDumpQuadlets( quadlet_t *data, unsigned int length ) +{ + unsigned int i=0; + + if ( length <= 0 ) { + return; + } + for (i = 0; i< length; i += 1) { + printf( "%02d %04X: %08X (%08X)" + "\n", i, i*4, data[i],ntohl(data[i])); + } +} + + Index: /branches/api-cleanup/src/libavc/descriptors/avc_descriptor_cmd.h =================================================================== --- /branches/api-cleanup/src/libavc/descriptors/avc_descriptor_cmd.h (revision 742) +++ /branches/api-cleanup/src/libavc/descriptors/avc_descriptor_cmd.h (revision 742) @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +#ifndef AVCDESCRIPTORCMD_H +#define AVCDESCRIPTORCMD_H + +#include "../general/avc_generic.h" + +#include + +namespace AVC { + + +// defined in avc1394.h +// #define AVC1394_CMD_OPEN_DESCRIPTOR 0x08 +// #define AVC1394_CMD_READ_DESCRIPTOR 0x09 +// #define AVC1394_CMD_WRITE_DESCRIPTOR 0x0A +// #define AVC1394_CMD_SEARCH_DESCRIPTOR 0x0B +// #define AVC1394_CMD_OBJECT_NUMBER_SELECT 0x0D + +// not yet defined +#define AVC1394_CMD_CREATE_DESCRIPTOR 0x0C +#define AVC1394_CMD_OPEN_INFOBLOCK 0x05 +#define AVC1394_CMD_READ_INFOBLOCK 0x06 +#define AVC1394_CMD_WRITE_INFOBLOCK 0x07 + +class AVCDescriptorSpecifier; + +class OpenDescriptorCmd: public AVCCommand +{ +public: + enum EMode { + eClose = 0x00, + eRead = 0x01, + eWrite = 0x03, + }; + + enum EStatus { + eReady = 0x00, + eReadOpened = 0x01, + eNonExistent = 0x04, + eListOnly = 0x05, + eAtCapacity = 0x11, + eWriteOpened = 0x33, + }; + + OpenDescriptorCmd(Ieee1394Service& ); + virtual ~OpenDescriptorCmd(); + + virtual bool serialize( Util::IOSSerialize& se ); + virtual bool deserialize( Util::IISDeserialize& de ); + + virtual bool clear(); + + virtual const char* getCmdName() const + { return "OpenDescriptorCmd"; } + + virtual void setMode( enum EMode m ) {m_mode=m;}; + AVCDescriptorSpecifier *m_specifier; + enum EMode m_mode; + + byte_t m_status; + byte_t m_reserved; + uint16_t m_locked_node_id; + +private: +}; + +class ReadDescriptorCmd: public AVCCommand +{ +public: + enum EReadStatus { + eComplete = 0x10, + eMoreToRead = 0x11, + eTooLarge = 0x12, + eInvalid = 0xFF, + }; + + ReadDescriptorCmd(Ieee1394Service& ieee1394service); + virtual ~ReadDescriptorCmd(); + + virtual bool serialize( Util::IOSSerialize& se ); + virtual bool deserialize( Util::IISDeserialize& de ); + + virtual bool clear(); + + enum EReadStatus getStatus(); + + virtual const char* getCmdName() const + { return "ReadDescriptorCmd"; } + + byte_t m_status; + byte_t m_reserved; + uint16_t m_data_length; + uint16_t m_address; + + byte_t *m_data; + + AVCDescriptorSpecifier *m_specifier; +private: + +}; + +} + +#endif // AVCDESCRIPTORCMD_H Index: /branches/api-cleanup/src/libavc/descriptors/avc_descriptor.cpp =================================================================== --- /branches/api-cleanup/src/libavc/descriptors/avc_descriptor.cpp (revision 742) +++ /branches/api-cleanup/src/libavc/descriptors/avc_descriptor.cpp (revision 742) @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2005-2007 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "avc_descriptor.h" +#include "avc_descriptor_cmd.h" + +#include "../general/avc_unit.h" +#include "../general/avc_subunit.h" + +#include "libutil/cmd_serialize.h" +#include "libieee1394/ieee1394service.h" +#include "libieee1394/configrom.h" + +namespace AVC { + +AVCDescriptorSpecifier::AVCDescriptorSpecifier( enum EType type ) + : m_type ( type ) + , m_listid_size ( 0 ) + , m_objectid_size ( 0 ) + , m_entrypos_size ( 0 ) + , m_info_block_type ( 0 ) + , m_info_block_instance ( 0 ) + , m_info_block_position ( 0 ) +{ + +} + +bool +AVCDescriptorSpecifier::serialize( Util::IOSSerialize& se ) +{ + se.write( (byte_t)m_type, "AVCDescriptorSpecifier descriptor_specifier_type" ); + switch ( m_type ) { + case eIndentifier: + // nothing to serialize + return true; + case eInfoBlockByType: + se.write( m_info_block_type, "AVCDescriptorSpecifier info_block_type" ); + se.write( m_info_block_instance, "AVCDescriptorSpecifier instance_count" ); + return true; + case eInfoBlockByPosition: + se.write( m_info_block_position, "AVCDescriptorSpecifier info_block_position" ); + return true; + case eSubunit0x80: + // nothing to serialize + return true; + case eInvalid: + default: + debugError("Unsupported Descriptor Specifier type: 0x%02X\n",m_type); + return false; + } +} + +bool +AVCDescriptorSpecifier::deserialize( Util::IISDeserialize& de ) +{ + de.read( (byte_t *)&m_type ); + switch ( m_type ) { + case eIndentifier: + // nothing to deserialize + return true; + case eInfoBlockByType: + de.read( &m_info_block_type); + de.read( &m_info_block_instance ); + case eInfoBlockByPosition: + de.read( &m_info_block_position); + + return true; + case eSubunit0x80: + // nothing to deserialize + return true; + case eInvalid: + default: + debugError("Unsupported Descriptor Specifier type: 0x%02X\n",m_type); + return false; + } + + return true; +} + +AVCDescriptorSpecifier* +AVCDescriptorSpecifier::clone() const +{ + return new AVCDescriptorSpecifier( *this ); +} + +//---------------------- +AVCDescriptor::AVCDescriptor( Unit* unit ) + : IBusData() + , m_unit( unit ) + , m_subunit ( NULL ) + , m_specifier ( AVCDescriptorSpecifier::eInvalid ) + , m_data ( NULL ) + , m_descriptor_length(0) + , m_loaded ( false ) +{ +} + +AVCDescriptor::AVCDescriptor( Unit* unit, Subunit* subunit ) + : IBusData() + , m_unit( unit ) + , m_subunit ( subunit ) + , m_specifier ( AVCDescriptorSpecifier::eInvalid ) + , m_data ( NULL ) + , m_descriptor_length(0) + , m_loaded ( false ) +{ +} + +AVCDescriptor::AVCDescriptor( Unit* unit, Subunit* subunit, + AVCDescriptorSpecifier s ) + : IBusData() + , m_unit( unit ) + , m_subunit ( subunit ) + , m_specifier ( s ) + , m_data ( NULL ) + , m_descriptor_length(0) + , m_loaded ( false ) +{ +} + +AVCDescriptor::~AVCDescriptor() +{ + if (m_data != NULL) free(m_data); +} +bool +AVCDescriptor::reload() +{ + m_loaded = false; + return load(); +} + +bool +AVCDescriptor::load() +{ + bool result; + + if (m_loaded) { + debugOutput(DEBUG_LEVEL_VERBOSE, "Descriptor already loaded, not re-loading...\n" ); + return true; + } + + OpenDescriptorCmd openDescCmd(m_unit->get1394Service()); + + debugOutput(DEBUG_LEVEL_VERBOSE, " Open descriptor (%s)\n",getDescriptorName()); + openDescCmd.setMode( OpenDescriptorCmd::eRead ); + openDescCmd.m_specifier=&m_specifier; + openDescCmd.setNodeId( m_unit->getConfigRom().getNodeId() ); + openDescCmd.setCommandType( AVCCommand::eCT_Control ); + openDescCmd.setSubunitType( getSubunitType() ); + openDescCmd.setSubunitId( getSubunitId() ); + openDescCmd.setVerbose( getVerboseLevel() ); + + result=openDescCmd.fire(); + + if (!result || (openDescCmd.getResponse() != AVCCommand::eR_Accepted)) { + debugOutput(DEBUG_LEVEL_VERBOSE, " Could not open descriptor\n"); + return false; + } + + debugOutput(DEBUG_LEVEL_VERBOSE, " Read descriptor\n"); + ReadDescriptorCmd readDescCmd(m_unit->get1394Service()); + readDescCmd.m_specifier=&m_specifier; + readDescCmd.setNodeId( m_unit->getConfigRom().getNodeId() ); + readDescCmd.setCommandType( AVCCommand::eCT_Control ); + readDescCmd.setSubunitType( getSubunitType() ); + readDescCmd.setSubunitId( getSubunitId() ); + readDescCmd.setVerbose( getVerboseLevel() ); + readDescCmd.m_data_length=2; + readDescCmd.m_address=0; + + result=readDescCmd.fire(); + + if (!result || (readDescCmd.getResponse() != AVCCommand::eR_Accepted)) { + debugOutput(DEBUG_LEVEL_VERBOSE, " Could not read descriptor\n"); + return false; + } + + size_t bytes_read=readDescCmd.m_data_length; + if (bytes_read < 2) { + debugOutput(DEBUG_LEVEL_VERBOSE, " Descriptor length field not present\n"); + return false; + } + +#ifdef DEBUG + if(getDebugLevel() >= DEBUG_LEVEL_VERY_VERBOSE) { + debugOutput(DEBUG_LEVEL_VERBOSE, " Read result:\n"); + printBufferBytes( DEBUG_LEVEL_VERY_VERBOSE, bytes_read, readDescCmd.m_data ); + } +#endif + + // obtain descriptor length + m_descriptor_length=(readDescCmd.m_data[0]<<8) + (readDescCmd.m_data[1]); + debugOutput(DEBUG_LEVEL_VERBOSE, " Descriptor length: %u\n", m_descriptor_length); + + if (m_data != NULL) free(m_data); + + m_data=(byte_t *)calloc(m_descriptor_length, 1); + if (m_data == NULL) { + debugError("Could not allocate memory for descriptor\n"); + return false; + } + + // we reread everything from here + bytes_read=0; + while(bytes_readgetConfigRom().getNodeId() ); + readDescCmd.setCommandType( AVCCommand::eCT_Control ); + readDescCmd.setSubunitType( getSubunitType() ); + readDescCmd.setSubunitId( getSubunitId() ); + readDescCmd.setVerbose( getVerboseLevel() ); + readDescCmd.m_data_length=m_descriptor_length-bytes_read; + // account for the length field + readDescCmd.m_address=bytes_read+2; + + result=readDescCmd.fire(); + + if (!result || (readDescCmd.getResponse() != AVCCommand::eR_Accepted)) { + debugOutput(DEBUG_LEVEL_VERBOSE, " Could not read descriptor data\n"); + return false; + } + + // copy the payload + + if (bytes_read+readDescCmd.m_data_length>m_descriptor_length) { + debugWarning("Device returned too much data, truncating\n"); + readDescCmd.m_data_length=m_descriptor_length-bytes_read; + } + + debugOutput(DEBUG_LEVEL_VERBOSE, " copying %u bytes to internal buffer offset %u\n",readDescCmd.m_data_length, bytes_read); + + memcpy(m_data+bytes_read,readDescCmd.m_data, readDescCmd.m_data_length); + bytes_read += readDescCmd.m_data_length; + + if((readDescCmd.getStatus() != ReadDescriptorCmd::eMoreToRead) + && ( bytes_readgetConfigRom().getNodeId() ); + openDescCmd.setCommandType( AVCCommand::eCT_Control ); + openDescCmd.setSubunitType( getSubunitType() ); + openDescCmd.setSubunitId( getSubunitId() ); + openDescCmd.setVerbose( getVerboseLevel() ); + + result=openDescCmd.fire(); + + if (!result || (openDescCmd.getResponse() != AVCCommand::eR_Accepted)) { + debugOutput(DEBUG_LEVEL_VERBOSE, " Could not close descriptor\n"); + return false; + } +#ifdef DEBUG + if(getDebugLevel() >= DEBUG_LEVEL_VERY_VERBOSE) { + debugOutput(DEBUG_LEVEL_VERBOSE, " Descriptor content:\n"); + printBufferBytes( DEBUG_LEVEL_VERY_VERBOSE, m_descriptor_length, m_data ); + } +#endif + debugOutput(DEBUG_LEVEL_VERBOSE, " Parse descriptor\n"); + // parse the descriptor + Util::BufferDeserialize de( m_data, m_descriptor_length ); + result = deserialize( de ); + if (!result) { + debugOutput(DEBUG_LEVEL_VERBOSE, " Could not parse descriptor\n"); + return false; + } + +#ifdef DEBUG + if(getDebugLevel() >= DEBUG_LEVEL_VERY_VERBOSE) { + Util::StringSerializer se_dbg; + serialize( se_dbg ); + + // output the debug message in smaller chunks to avoid problems + // with a max message size + unsigned int chars_to_write=se_dbg.getString().size(); + unsigned int chars_written=0; + while (chars_writtengetSubunitType()); +} + +subunit_id_t +AVCDescriptor::getSubunitId() const +{ + return (m_subunit==NULL?0xFF:m_subunit->getSubunitId()); +} + +bool +AVCDescriptor::setVerboseLevel( int verboseLevel ) +{ + setDebugLevel(verboseLevel); + return true; +} + +int +AVCDescriptor::getVerboseLevel() +{ + return getDebugLevel(); +} + +void +AVCDescriptor::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"); +} + +// --- Info block +AVCInfoBlock::AVCInfoBlock( ) + : IBusData() + , m_compound_length ( 0 ) + , m_info_block_type ( 0 ) + , m_primary_field_length ( 0 ) + , m_supported_info_block_type ( 0xFFFF ) +{} + +AVCInfoBlock::AVCInfoBlock( uint16_t supported_type ) + : IBusData() + , m_compound_length ( 0 ) + , m_info_block_type ( 0 ) + , m_primary_field_length ( 0 ) + , m_supported_info_block_type ( supported_type ) +{} + +bool +AVCInfoBlock::serialize( Util::IOSSerialize& se ) +{ + bool result=true; + if((m_supported_info_block_type != 0xFFFF) + && (m_info_block_type != m_supported_info_block_type)) { + debugError("%s: Incorrect block type: 0x%04X, should be 0x%04X\n", + getInfoBlockName(), m_info_block_type, m_supported_info_block_type); + return false; + } + result &= se.write( m_compound_length, "AVCInfoBlock m_compound_length" ); + result &= se.write( m_info_block_type, "AVCInfoBlock m_info_block_type" ); + result &= se.write( m_primary_field_length, "AVCInfoBlock m_primary_field_length" ); + return result; +} + +bool +AVCInfoBlock::deserialize( Util::IISDeserialize& de ) +{ + bool result=true; + result &= de.read( &m_compound_length ); + result &= de.read( &m_info_block_type ); + result &= de.read( &m_primary_field_length ); + + if((m_supported_info_block_type != 0xFFFF) + && (m_info_block_type != m_supported_info_block_type)) { + debugError("%s: Incorrect block type: 0x%04X, should be 0x%04X\n", + getInfoBlockName(), m_info_block_type, m_supported_info_block_type); + return false; + } + + debugOutput(DEBUG_LEVEL_VERBOSE, "%s length=0x%04X (%u), type=0x%04X, primary field length=0x%04X (%u)\n", + getInfoBlockName(), m_compound_length, m_compound_length, + m_info_block_type, m_primary_field_length, m_primary_field_length); + + return result; +} + +bool +AVCInfoBlock::peekBlockType( Util::IISDeserialize& de, uint16_t *type ) +{ + return de.peek(type, 2); +} + +bool +AVCInfoBlock::peekBlockLength( Util::IISDeserialize& de, uint16_t *type ) +{ + return de.peek(type, 0); +} + +AVCInfoBlock* +AVCInfoBlock::clone() const +{ + return new AVCInfoBlock( *this ); +} +bool +AVCInfoBlock::setVerbose( int verboseLevel ) +{ + setDebugLevel(verboseLevel); + return true; +} + +int +AVCInfoBlock::getVerboseLevel() +{ + return getDebugLevel(); +} + +// --------- + +//FIXME: find out the correct id for this +AVCRawTextInfoBlock::AVCRawTextInfoBlock( ) + : AVCInfoBlock( 0x000A ) +{} + +AVCRawTextInfoBlock::~AVCRawTextInfoBlock( ) +{ + clear(); +} + +bool +AVCRawTextInfoBlock::clear() +{ + return true; +} + +bool +AVCRawTextInfoBlock::serialize( Util::IOSSerialize& se ) +{ + bool result=true; + result &= AVCInfoBlock::serialize(se); + if (m_text.size()) { + se.write(m_text.c_str(),m_text.size(), "AVCRawTextInfoBlock text"); + } + return result; +} + +bool +AVCRawTextInfoBlock::deserialize( Util::IISDeserialize& de ) +{ + bool result=true; + result &= AVCInfoBlock::deserialize(de); + + // note that the pointer returned by de.read is not valid outside this function + // but since we add it to m_text it's not a problem + char *txt; + result &= de.read(&txt,m_compound_length-4); + m_text.clear(); + m_text.append(txt); + + debugOutput(DEBUG_LEVEL_VERBOSE, "Read AVCRawTextInfoBlock: '%s'\n", m_text.c_str()); + + return result; +} + +// --------- + +AVCNameInfoBlock::AVCNameInfoBlock( ) + : AVCInfoBlock( 0x000B ) +{} + +AVCNameInfoBlock::~AVCNameInfoBlock( ) +{ + clear(); +} + +bool +AVCNameInfoBlock::clear() +{ + return true; +} + +bool +AVCNameInfoBlock::serialize( Util::IOSSerialize& se ) +{ + bool result=true; + result &= AVCInfoBlock::serialize(se); + + if (m_text.size()) { + result &= se.write((uint16_t)0x0000, "AVCNameInfoBlock unknown"); + result &= se.write((uint16_t)0x0000, "AVCNameInfoBlock unknown"); + result &= se.write((uint16_t)0x0000, "AVCNameInfoBlock unknown length"); + result &= se.write((uint16_t)0x0000, "AVCNameInfoBlock unknown"); + result &= se.write((uint16_t)m_text.size(), "AVCNameInfoBlock text length"); + + se.write(m_text.c_str(),m_text.size(), "AVCNameInfoBlock text"); + } + return result; +} + +bool +AVCNameInfoBlock::deserialize( Util::IISDeserialize& de ) +{ + bool result=true; + result &= AVCInfoBlock::deserialize(de); + + // FIXME: get the spec somewhere to do this correctly + uint16_t dummy16; + uint16_t length1; + uint16_t text_length; + + result &= de.read(&dummy16); + result &= de.read(&dummy16); + result &= de.read(&length1); + result &= de.read(&dummy16); + result &= de.read(&text_length); + + // note that the pointer returned by de.read is not valid outside this function + // but since we add it to m_text it's not a problem + char *txt; + result &= de.read(&txt,text_length); + m_text.clear(); + m_text.append(txt); + + debugOutput(DEBUG_LEVEL_VERBOSE, "Read AVCNameInfoBlock: '%s'\n", m_text.c_str()); + + return result; +} + +} Index: /branches/api-cleanup/src/libavc/descriptors/avc_descriptor_cmd.cpp =================================================================== --- /branches/api-cleanup/src/libavc/descriptors/avc_descriptor_cmd.cpp (revision 742) +++ /branches/api-cleanup/src/libavc/descriptors/avc_descriptor_cmd.cpp (revision 742) @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2005-2007 by Pieter Palmers + * Copyright (C) 2005-2007 by Daniel Wagner + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "avc_descriptor_cmd.h" +#include "avc_descriptor.h" +#include "libutil/cmd_serialize.h" +#include "libieee1394/ieee1394service.h" + +#include +#include + +using namespace std; + +namespace AVC { + +OpenDescriptorCmd::OpenDescriptorCmd(Ieee1394Service& ieee1394service) + : AVCCommand( ieee1394service, AVC1394_CMD_OPEN_DESCRIPTOR ) + , m_specifier( NULL ) + , m_mode( eClose ) + , m_status ( 0xFF ) + , m_reserved ( 0x00 ) + , m_locked_node_id ( 0xFFFF ) +{ +} + +OpenDescriptorCmd::~OpenDescriptorCmd() +{ +} + +bool +OpenDescriptorCmd::clear() +{ + m_status = 0xFF; + m_reserved = 0x00; + m_locked_node_id = 0xFFFF; + return true; +} + +bool +OpenDescriptorCmd::serialize( Util::IOSSerialize& se ) +{ + AVCCommand::serialize( se ); + + if(m_specifier==NULL) { + debugError("m_specifier==NULL"); + return false; + } + + m_specifier->serialize( se ); + + switch (getCommandType()) { + case eCT_Status: + se.write( (byte_t)m_status, "OpenDescriptorCmd status" ); + se.write( (byte_t)m_reserved, "OpenDescriptorCmd reserved" ); + se.write( (uint16_t)m_locked_node_id, "OpenDescriptorCmd node_id" ); + break; + case eCT_Control: + se.write( (byte_t)m_mode, "OpenDescriptorCmd subfunction" ); + se.write( (byte_t)m_reserved, "OpenDescriptorCmd reserved" ); + break; + default: + debugError("Unsupported type for this command: %02X\n", getCommandType()); + return false; + } + return true; +} + +bool +OpenDescriptorCmd::deserialize( Util::IISDeserialize& de ) +{ + AVCCommand::deserialize( de ); + + if(m_specifier==NULL) { + debugError("m_specifier==NULL"); + return false; + } + + m_specifier->deserialize( de ); + + switch ( getCommandType() ) { + case eCT_Status: + de.read( &m_status ); + de.read( &m_reserved ); + de.read( &m_locked_node_id ); + + break; + case eCT_Control: + de.read( &m_status ); + de.read( &m_reserved ); + switch (m_status) { + case (byte_t)eClose: m_mode=eClose; break; + case (byte_t)eRead: m_mode=eRead; break; + case (byte_t)eWrite: m_mode=eWrite; break; + default: + debugError("Unknown response subfunction 0x%02X\n", m_status); + } + + break; + default: + debugError("Can't handle command type %s\n", getCommandType()); + return false; + } + + + return true; +} + +// + +ReadDescriptorCmd::ReadDescriptorCmd(Ieee1394Service& ieee1394service) + : AVCCommand( ieee1394service, AVC1394_CMD_READ_DESCRIPTOR ) + , m_status ( 0xFF ) + , m_reserved ( 0xFF ) + , m_data_length ( 0 ) + , m_address ( 0 ) + , m_data ( NULL ) + , m_specifier( NULL ) +{ +} + +ReadDescriptorCmd::~ReadDescriptorCmd() +{ + delete[] m_data; +} + +bool +ReadDescriptorCmd::clear() +{ + m_status = 0xFF; + m_reserved = 0x00; + m_data_length = 0x0000; + m_address = 0x0000; + delete[] m_data; + m_data = NULL; + return true; +} + +bool +ReadDescriptorCmd::serialize( Util::IOSSerialize& se ) +{ + AVCCommand::serialize( se ); + + if(m_specifier==NULL) { + debugError("m_specifier==NULL"); + return false; + } + + m_specifier->serialize( se ); + + switch (getCommandType()) { + case eCT_Control: + se.write( (byte_t)m_status, "ReadDescriptorCmd read_result_status" ); + se.write( (byte_t)m_reserved, "ReadDescriptorCmd reserved" ); + se.write( (uint16_t)m_data_length, "ReadDescriptorCmd data_length" ); + se.write( (uint16_t)m_address, "ReadDescriptorCmd address" ); + + break; + default: + debugError("Unsupported type for this command: %02X\n", getCommandType()); + return false; + } + return true; +} + +bool +ReadDescriptorCmd::deserialize( Util::IISDeserialize& de ) +{ + AVCCommand::deserialize( de ); + + if(m_specifier==NULL) { + debugError("m_specifier==NULL"); + return false; + } + + m_specifier->deserialize( de ); + + switch (getCommandType()) { + case eCT_Control: + de.read( (byte_t *)&m_status ); + de.read( (byte_t *)&m_reserved ); + de.read( (uint16_t *)&m_data_length ); + de.read( (uint16_t *)&m_address ); + + if (getResponse()==eR_Accepted) { + if (m_data_length>0) { + // the pointer returned by de.read is not valid outside this function + // hence we copy the data to an internal buffer + m_data = new byte_t[m_data_length]; + if(m_data == NULL) { + debugError("Could not allocate memory for payload data"); + return false; + } + char * cmd_data = NULL; + if (!de.read( (char **)&cmd_data, m_data_length )) { + delete[] m_data; + m_data = NULL; + debugError("Could not read payload data"); + return false; + } + memcpy(m_data, cmd_data, m_data_length); + + } else { + debugWarning("Read descriptor command accepted but no payload data returned.\n"); + m_data=NULL; + } + } + break; + default: + debugError("Unsupported type for this command: %02X\n", getCommandType()); + return false; + } + + return true; +} + +enum ReadDescriptorCmd::EReadStatus +ReadDescriptorCmd::getStatus() +{ + switch(m_status) { + case 0x10: return eComplete; + case 0x11: return eMoreToRead; + case 0x12: return eTooLarge; + default: return eInvalid; + } +} + +} Index: /branches/api-cleanup/src/libavc/descriptors/avc_descriptor.h =================================================================== --- /branches/api-cleanup/src/libavc/descriptors/avc_descriptor.h (revision 742) +++ /branches/api-cleanup/src/libavc/descriptors/avc_descriptor.h (revision 742) @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2005-2007 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 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 . + * + */ + +/** + * Partially implements AV/C Descriptors/InfoBlocks as in TA2001021 + * + * The idea is to treat a descriptor as an object that can fetch and store + * it's state from/to a device. It will call the necessary AV/C commands to + * achieve this. This (hopefully) simplifies handling the fact that there are + * so many different descriptor types. It also handles the fact that descriptors + * are not state-less. + * + */ + +#ifndef AVCDESCRIPTOR_H +#define AVCDESCRIPTOR_H + +#include "../avc_definitions.h" + +#include "../general/avc_generic.h" +#include "debugmodule/debugmodule.h" + +#include +#include + +class Ieee1394Service; + +namespace AVC { + +class Unit; +class Subunit; + +class Util::IOSSerialize; +class Util::IISDeserialize; +/** + * The specifier used to indicate the target descriptor + */ + +// NOTE: how are we going to do this? all lengths of the +// arguments are dependent on the (sub)unit descriptor +class AVCDescriptorSpecifier : public IBusData +{ +public: + enum EType { + eIndentifier = 0x00, + eListById = 0x10, + eListByType = 0x11, + eEntryByListId = 0x20, + eEntryByObjectIdInList = 0x21, + eEntryByType = 0x22, + eEntryByObjectId = 0x23, + eInfoBlockByType = 0x30, + eInfoBlockByPosition = 0x31, + eSubunit0x80 = 0x80, + eInvalid = 0xFF, + }; + +public: + AVCDescriptorSpecifier( enum EType type ); + virtual ~AVCDescriptorSpecifier() {}; + + virtual bool serialize( Util::IOSSerialize& se ); + virtual bool deserialize( Util::IISDeserialize& de ); + + virtual AVCDescriptorSpecifier* clone() const; + +/* void setType( enum EType type ) {m_type=type;}; + void setListIdSize( unsigned int l ) {m_listid_size=l;}; + void setObjectIdSize( unsigned int l ) {m_objectid_size=l;}; + void setEntryPositionSize( unsigned int l ) {m_entrypos_size=l;};*/ + + enum EType m_type; + uint16_t m_listid_size; + uint16_t m_objectid_size; + uint16_t m_entrypos_size; + + uint16_t m_info_block_type; + byte_t m_info_block_instance; + byte_t m_info_block_position; + +private: + + +}; + +/** + * The descriptor class + */ +class AVCDescriptor : public IBusData +{ +public: + + virtual bool serialize( Util::IOSSerialize& se ); + virtual bool deserialize( Util::IISDeserialize& de ); + + // note: in the end these have to be protected + AVCDescriptor( Unit* unit ); + AVCDescriptor( Unit* unit, Subunit* subunit ); + AVCDescriptor( Unit* unit, Subunit* subunit, AVCDescriptorSpecifier s ); + virtual ~AVCDescriptor(); + + virtual AVCDescriptor* clone() const; + + void setSpecifier(AVCDescriptorSpecifier s) {m_specifier=s;}; + + ESubunitType getSubunitType() const; + subunit_id_t getSubunitId() const; + + bool setVerboseLevel( int verboseLevel ); + int getVerboseLevel(); + + virtual const char* getDescriptorName() const + {return "AVCDescriptor";}; + + bool load(); + bool reload(); + +protected: + void printBufferBytes(unsigned int level, size_t length, byte_t* buffer) const; + + Unit* m_unit; + Subunit* m_subunit; + + AVCDescriptorSpecifier m_specifier; + + byte_t* m_data; + uint16_t m_descriptor_length; + + bool m_loaded; + +}; + +/** + * The info block class + */ +class AVCInfoBlock : public IBusData +{ +public: + + virtual bool serialize( Util::IOSSerialize& se ); + virtual bool deserialize( Util::IISDeserialize& de ); + static bool peekBlockType( Util::IISDeserialize& de, uint16_t * ); + static bool peekBlockLength( Util::IISDeserialize& de, uint16_t * ); + + // note: in the end these have to be protected + AVCInfoBlock( ); + AVCInfoBlock( uint16_t ); + virtual ~AVCInfoBlock() {}; + + virtual AVCInfoBlock* clone() const; + +// EInfoBlockType getType(); + + bool setVerbose( int verboseLevel ); + int getVerboseLevel(); + + virtual const char* getInfoBlockName() const + {return "AVCInfoBlock";}; + + uint16_t m_compound_length; + uint16_t m_info_block_type; + uint16_t m_primary_field_length; + + uint16_t m_supported_info_block_type; +private: + +}; + +class AVCRawTextInfoBlock : public AVCInfoBlock +{ +public: + + virtual bool serialize( Util::IOSSerialize& se ); + virtual bool deserialize( Util::IISDeserialize& de ); + + virtual bool clear(); + + AVCRawTextInfoBlock( ); + virtual ~AVCRawTextInfoBlock(); + virtual const char* getInfoBlockName() const + {return "AVCRawTextInfoBlock";}; + + std::string m_text; + +protected: + +private: + +}; + +class AVCNameInfoBlock : public AVCInfoBlock +{ +public: + + virtual bool serialize( Util::IOSSerialize& se ); + virtual bool deserialize( Util::IISDeserialize& de ); + + virtual bool clear(); + + AVCNameInfoBlock( ); + virtual ~AVCNameInfoBlock(); + virtual const char* getInfoBlockName() const + {return "AVCNameInfoBlock";}; + + std::string m_text; + +protected: + +private: + +}; +/** + * + */ +// class AVCUnitIdentifierDescriptor : public AVCDescriptor +// { +// +// public: +// AVCUnitIdentifierDescriptor( ); +// virtual ~AVCUnitIdentifierDescriptor() {} +// +// }; + +} + +#endif // AVCDESCRIPTOR_H Index: /branches/api-cleanup/src/libavc/ccm/avc_signal_source.cpp =================================================================== --- /branches/api-cleanup/src/libavc/ccm/avc_signal_source.cpp (revision 742) +++ /branches/api-cleanup/src/libavc/ccm/avc_signal_source.cpp (revision 742) @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "avc_signal_source.h" +#include "libutil/cmd_serialize.h" +#include "libieee1394/ieee1394service.h" + +#include +#include + +using namespace std; + +#define AVC1394_CMD_SIGNAL_SOURCE 0x1A + +namespace AVC { + + +SignalUnitAddress::SignalUnitAddress() + : m_plugId( ePI_Invalid ) +{ +} + +bool +SignalUnitAddress::serialize( Util::IOSSerialize& se ) +{ + byte_t reserved = 0xff; + se.write( reserved, "SignalUnitAddress" ); + se.write( m_plugId, "SignalUnitAddress plugId" ); + return true; +} + +bool +SignalUnitAddress::deserialize( Util::IISDeserialize& de ) +{ + byte_t operand; + de.read( &operand ); + de.read( &m_plugId ); + return true; +} + +SignalUnitAddress* +SignalUnitAddress::clone() const +{ + return new SignalUnitAddress( *this ); +} + +//////////////////////////////////////// + +SignalSubunitAddress::SignalSubunitAddress() + : m_subunitType( AVC1394_SUBUNIT_RESERVED ) + , m_subunitId( AVC1394_SUBUNIT_ID_RESERVED ) + , m_plugId( ePI_Invalid ) +{ +} + +bool +SignalSubunitAddress::serialize( Util::IOSSerialize& se ) +{ + byte_t operand = ( m_subunitType << 3 ) | ( m_subunitId & 0x7 ); + se.write( operand, "SignalSubunitAddress subunitType & subunitId" ); + se.write( m_plugId, "SignalSubunitAddress plugId" ); + return true; +} + +bool +SignalSubunitAddress::deserialize( Util::IISDeserialize& de ) +{ + byte_t operand; + de.read( &operand ); + m_subunitType = operand >> 3; + m_subunitId = operand & 0x7; + de.read( &m_plugId ); + return true; +} + +SignalSubunitAddress* +SignalSubunitAddress::clone() const +{ + return new SignalSubunitAddress( *this ); +} + +//////////////////////////////////////// + + +SignalSourceCmd::SignalSourceCmd( Ieee1394Service& ieee1394service ) + : AVCCommand( ieee1394service, AVC1394_CMD_SIGNAL_SOURCE ) + , m_resultStatus( 0xff ) + , m_outputStatus( 0xff ) + , m_conv( 0xff ) + , m_signalStatus( 0xff ) + , m_signalSource( 0 ) + , m_signalDestination( 0 ) +{ +} + +SignalSourceCmd::~SignalSourceCmd() +{ + delete m_signalSource; + m_signalSource = 0; + delete m_signalDestination; + m_signalDestination = 0; +} + +bool +SignalSourceCmd::serialize( Util::IOSSerialize& se ) +{ + AVCCommand::serialize( se ); + + byte_t operand; + switch ( getCommandType() ) { + case eCT_Status: + operand = ( m_outputStatus << 5 ) + | ( ( m_conv & 0x1 ) << 4 ) + | ( m_signalStatus & 0xf ); + se.write( operand, "SignalSourceCmd outputStatus & conv & signalStatus" ); + break; + case eCT_Control: + case eCT_SpecificInquiry: + operand = m_resultStatus & 0xf; + se.write( operand, "SignalSourceCmd resultStatus" ); + break; + default: + cerr << "Can't handle command type " << getCommandType() << endl; + return false; + } + + switch( getSubunitType() ) { + case eST_Unit: + case eST_Audio: + case eST_Music: + { + if ( m_signalSource ) { + m_signalSource->serialize( se ); + } else { + byte_t reserved = 0xff; + se.write( reserved, "SignalSourceCmd" ); + se.write( reserved, "SignalSourceCmd" ); + } + + if ( m_signalDestination ) { + m_signalDestination->serialize( se ); + } else { + byte_t reserved = 0xff; + se.write( reserved, "SignalSourceCmd" ); + se.write( reserved, "SignalSourceCmd" ); + } + } + break; + default: + cerr << "Can't handle subunit type " << getSubunitType() << endl; + return false; + } + + return true; +} + +bool +SignalSourceCmd::deserialize( Util::IISDeserialize& de ) +{ + delete m_signalSource; + m_signalSource = 0; + delete m_signalDestination; + m_signalDestination = 0; + + AVCCommand::deserialize( de ); + + byte_t operand; + switch ( getCommandType() ) { + case eCT_Status: + de.read( &operand ); + m_outputStatus = operand >> 5; + m_conv = ( operand & 0x10 ) >> 4; + m_signalStatus = operand & 0xf; + break; + case eCT_Control: + case eCT_SpecificInquiry: + de.read( &operand ); + m_resultStatus = operand & 0xf; + break; + default: + cerr << "Can't handle command type " << getCommandType() << endl; + return false; + } + + switch( getSubunitType() ) { + case eST_Unit: + case eST_Audio: + case eST_Music: + { + byte_t operand; + de.peek( &operand ); + if ( operand == 0xff ) { + m_signalSource = new SignalUnitAddress; + } else { + m_signalSource = new SignalSubunitAddress; + } + + m_signalSource->deserialize( de ); + + de.peek( &operand ); + if ( operand == 0xff ) { + m_signalDestination = new SignalUnitAddress; + } else { + m_signalDestination = new SignalSubunitAddress; + } + m_signalDestination->deserialize( de ); + } + break; + default: + cerr << "Can't handle subunit type " << getSubunitType() << endl; + return false; + } + + return true; +} + +bool +SignalSourceCmd::setSignalSource( SignalUnitAddress& signalAddress ) +{ + if ( m_signalSource ) { + delete m_signalSource; + } + m_signalSource = signalAddress.clone(); + return true; +} + +bool +SignalSourceCmd::setSignalSource( SignalSubunitAddress& signalAddress ) +{ + if ( m_signalSource ) { + delete m_signalSource; + } + m_signalSource = signalAddress.clone(); + return true; +} + +bool +SignalSourceCmd::setSignalDestination( SignalUnitAddress& signalAddress ) +{ + if ( m_signalDestination ) { + delete m_signalDestination; + } + m_signalDestination = signalAddress.clone(); + return true; +} + +bool +SignalSourceCmd::setSignalDestination( SignalSubunitAddress& signalAddress ) +{ + if ( m_signalDestination ) { + delete m_signalDestination; + } + m_signalDestination = signalAddress.clone(); + return true; +} + +SignalAddress* +SignalSourceCmd::getSignalSource() +{ + return m_signalSource; +} + +SignalAddress* +SignalSourceCmd::getSignalDestination() +{ + return m_signalDestination; +} + +} Index: /branches/api-cleanup/src/libavc/ccm/avc_signal_source.h =================================================================== --- /branches/api-cleanup/src/libavc/ccm/avc_signal_source.h (revision 742) +++ /branches/api-cleanup/src/libavc/ccm/avc_signal_source.h (revision 742) @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef AVCSIGNALSOURCE_H +#define AVCSIGNALSOURCE_H + +#include "../general/avc_generic.h" +#include "../avc_definitions.h" + +#include + +namespace AVC { + + +class SignalAddress: public IBusData +{ +public: + enum EPlugId { + ePI_AnyAvailableSerialBusPlug = 0x7e, + ePI_Invalid = 0xfe, + ePI_AnyAvailableExternalPlug = 0xff, + }; +}; + +class SignalUnitAddress: public SignalAddress +{ +public: + SignalUnitAddress(); + + virtual bool serialize( Util::IOSSerialize& se ); + virtual bool deserialize( Util::IISDeserialize& de ); + virtual SignalUnitAddress* clone() const; + + byte_t m_plugId; +}; + +class SignalSubunitAddress: public SignalAddress +{ +public: + SignalSubunitAddress(); + + virtual bool serialize( Util::IOSSerialize& se ); + virtual bool deserialize( Util::IISDeserialize& de ); + virtual SignalSubunitAddress* clone() const; + + byte_t m_subunitType; + byte_t m_subunitId; + byte_t m_plugId; +}; + +class SignalSourceCmd: public AVCCommand +{ +public: + SignalSourceCmd( Ieee1394Service& ieee1394service ); + virtual ~SignalSourceCmd(); + + virtual bool serialize( Util::IOSSerialize& se ); + virtual bool deserialize( Util::IISDeserialize& de ); + + virtual const char* getCmdName() const + { return "SignalSourceCmd"; } + + bool setSignalSource( SignalUnitAddress& signalAddress ); + bool setSignalSource( SignalSubunitAddress& signalAddress ); + bool setSignalDestination( SignalUnitAddress& signalAddress ); + bool setSignalDestination( SignalSubunitAddress& signalAddress ); + + SignalAddress* getSignalSource(); + SignalAddress* getSignalDestination(); + + // Control response + byte_t m_resultStatus; + + // Status response + byte_t m_outputStatus; + byte_t m_conv; + byte_t m_signalStatus; + + SignalAddress* m_signalSource; + SignalAddress* m_signalDestination; +}; + +} + +#endif // AVCSIGNALSOURCE_H Index: /branches/api-cleanup/src/libavc/streamformat/avc_extended_stream_format.h =================================================================== --- /branches/api-cleanup/src/libavc/streamformat/avc_extended_stream_format.h (revision 742) +++ /branches/api-cleanup/src/libavc/streamformat/avc_extended_stream_format.h (revision 742) @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef AVCEXTENDEDSTREAMFROMAT_H +#define AVCEXTENDEDSTREAMFROMAT_H + +#include "../general/avc_generic.h" +#include "../general/avc_extended_cmd_generic.h" + +#include +#include +#include + +namespace AVC { + +#define AVC1394_STREAM_FORMAT_SUPPORT 0x2F +#define AVC1394_STREAM_FORMAT_SUBFUNCTION_INPUT 0x00 +#define AVC1394_STREAM_FORMAT_SUBFUNCTION_OUTPUT 0x01 + +// BridgeCo extensions +#define AVC1394_STREAM_FORMAT_SUBFUNCTION_EXTENDED_STREAM_FORMAT 0xC0 +#define AVC1394_STREAM_FORMAT_SUBFUNCTION_EXTENDED_STREAM_FORMAT_LIST 0xC1 + + +#define AVC1394_STREAM_FORMAT_HIERARCHY_ROOT_DVCR 0x80 +#define AVC1394_STREAM_FORMAT_HIERARCHY_ROOT_AUDIOMUSIC 0x90 +#define AVC1394_STREAM_FORMAT_HIERARCHY_ROOT_INVALID 0xFF + +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_DVCR_SD525_60 0x00 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_DVCR_SDL525_60 0x04 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_DVCR_HD1125_60 0x08 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_DVCR_SD625_60 0x80 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_DVCR_SDL625_50 0x84 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_DVCR_HD1250_50 0x88 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_AUDIOMUSIC_AM824 0x00 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_AUDIOMUSIC_24_4_AUDIO_PACK 0x01 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_AUDIOMUSIC_32_FLOATING_POINT_DATA 0x02 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_AUDIOMUSIC_AM824_COMPOUND 0x40 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_AUDIOMUSIC_DONT_CARE 0xFF + + +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_IEC60968_3 0x00 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_IEC61937_3 0x01 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_IEC61937_4 0x02 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_IEC61937_5 0x03 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_IEC61937_6 0x04 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_IEC61937_7 0x05 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_MULTI_BIT_LINEAR_AUDIO_RAW 0x06 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_MULTI_BIT_LINEAR_AUDIO_DVD_AUDIO 0x07 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_ONE_BIT_AUDIO_PLAIN_RAW 0x08 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_ONE_BIT_AUDIO_PLAIN_SACD 0x09 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_ONE_BIT_AUDIO_ENCODED_RAW 0x0A +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_ONE_BIT_AUDIO_ENCODED_SACD 0x0B +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_HIGH_PRECISION_MULTIBIT_LINEAR_AUDIO 0x0C +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_MIDI_CONFORMANT 0x0D +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_SYNC_STREAM 0x40 +#define AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_DONT_CARE 0xFF + +#define AVC1394_STREAM_FORMAT_AM824_IEC60968_3 0x00 +#define AVC1394_STREAM_FORMAT_AM824_IEC61937_3 0x01 +#define AVC1394_STREAM_FORMAT_AM824_IEC61937_4 0x02 +#define AVC1394_STREAM_FORMAT_AM824_IEC61937_5 0x03 +#define AVC1394_STREAM_FORMAT_AM824_IEC61937_6 0x04 +#define AVC1394_STREAM_FORMAT_AM824_IEC61937_7 0x05 +#define AVC1394_STREAM_FORMAT_AM824_MULTI_BIT_LINEAR_AUDIO_RAW 0x06 +#define AVC1394_STREAM_FORMAT_AM824_MULTI_BIT_LINEAR_AUDIO_DVD_AUDIO 0x07 +#define AVC1394_STREAM_FORMAT_AM824_HIGH_PRECISION_MULTIBIT_LINEAR_AUDIO 0x0C +#define AVC1394_STREAM_FORMAT_AM824_MIDI_CONFORMANT 0x0D +#define AVC1394_STREAM_FORMAT_AM824_DONT_CARE 0xFF + +/* +// As defined in 'AV/C Stream Format Information Specification 1.0 TA Document 2001002' +// Not used for extended stream format +#define AVC1394_STREAM_FORMAT_SUPPORT_STATUS_SUPPORTED_AND_CONFIGURED 0x00 +#define AVC1394_STREAM_FORMAT_SUPPORT_STATUS_SUPPORTED_AND_HAS_NOT_BEEN_CONFIGURED 0x01 +#define AVC1394_STREAM_FORMAT_SUPPORT_STATUS_SUPPORTED_AND_READY_TO_CONFIGURE 0x02 +#define AVC1394_STREAM_FORMAT_SUPPORT_STATUS_SUPPORTED_AND_NOT_CONFIGURED 0x03 +#define AVC1394_STREAM_FORMAT_SUPPORT_STATUS_NOT_SUPPORTED 0x04 +// 0x05 - 0xFE reserved +#define AVC1394_STREAM_FORMAT_SUPPORT_STATUS_NO_INFORMATION 0xFF +*/ + +#define AVC1394_EXTENDED_STREAM_FORMAT_INFO_STATUS_ACTIVE 0x00 +#define AVC1394_EXTENDED_STREAM_FORMAT_INFO_STATUS_INACTIVE 0x01 +#define AVC1394_EXTENDED_STREAM_FORMAT_INFO_STATUS_NO_STREAM_FORMAT 0x02 +#define AVC1394_EXTENDED_STREAM_FORMAT_INFO_STATUS_NOT_USED 0xff + +class Util::IOSSerialize; +class Util::IISDeserialize; + +enum ERateControl { + eRC_Supported = 0x00, + eRC_DontCare = 0x01, +}; + +//////////////////////////////////////////////////////////// + +class StreamFormatInfo: public IBusData +{ +public: + StreamFormatInfo(); + + virtual bool serialize( Util::IOSSerialize& se ); + virtual bool deserialize( Util::IISDeserialize& de ); + + virtual StreamFormatInfo* clone() const; + + number_of_channels_t m_numberOfChannels; + stream_format_t m_streamFormat; +}; +std::ostream& operator<<( std::ostream& stream, StreamFormatInfo info ); + +//////////////////////////////////////////////////////////// + +class FormatInformationStreams: public IBusData +{ +public: + FormatInformationStreams() {} + virtual ~FormatInformationStreams() {} +}; + +//////////////////////////////////////////////////////////// + +class FormatInformationStreamsSync: public FormatInformationStreams +{ +public: + FormatInformationStreamsSync(); + + virtual bool serialize( Util::IOSSerialize& se ); + virtual bool deserialize( Util::IISDeserialize& de ); + virtual FormatInformationStreamsSync* clone() const; + + reserved_t m_reserved0; + sampling_frequency_t m_samplingFrequency; + rate_control_t m_rateControl; + reserved_t m_reserved1; +}; + +//////////////////////////////////////////////////////////// + +class FormatInformationStreamsCompound: public FormatInformationStreams +{ +public: + FormatInformationStreamsCompound(); + virtual ~FormatInformationStreamsCompound(); + + virtual bool serialize( Util::IOSSerialize& se ); + virtual bool deserialize( Util::IISDeserialize& de ); + virtual FormatInformationStreamsCompound* clone() const; + + sampling_frequency_t m_samplingFrequency; + rate_control_t m_rateControl; + number_of_stream_format_infos_t m_numberOfStreamFormatInfos; + + typedef std::vector< StreamFormatInfo* > StreamFormatInfoVector; + StreamFormatInfoVector m_streamFormatInfos; +}; +std::ostream& operator<<( std::ostream& stream, FormatInformationStreamsCompound info ); + + +//////////////////////////////////////////////////////////// + +class FormatInformation: public IBusData +{ +public: + enum EFormatHierarchyRoot { + eFHR_DVCR = AVC1394_STREAM_FORMAT_HIERARCHY_ROOT_DVCR, + eFHR_AudioMusic = AVC1394_STREAM_FORMAT_HIERARCHY_ROOT_AUDIOMUSIC, + eFHR_Invalid = AVC1394_STREAM_FORMAT_HIERARCHY_ROOT_INVALID, + }; + + enum EFomatHierarchyLevel1 { + eFHL1_DVCR_SD525_60 = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_DVCR_SD525_60, + eFHL1_DVCR_SDL525_60 = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_DVCR_SDL525_60, + eFHL1_DVCR_HD1125_60 = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_DVCR_HD1125_60, + eFHL1_DVCR_SD625_60 = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_DVCR_SD625_60, + eFHL1_DVCR_SDL625_50 = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_DVCR_SDL625_50, + eFHL1_DVCR_HD1250_50 = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_DVCR_HD1250_50, + eFHL1_AUDIOMUSIC_AM824 = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_AUDIOMUSIC_AM824, + eFHL1_AUDIOMUSIC_24_4_AUDIO_PACK = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_AUDIOMUSIC_24_4_AUDIO_PACK, + eFHL1_AUDIOMUSIC_32_FLOATING = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_AUDIOMUSIC_32_FLOATING_POINT_DATA, + eFHL1_AUDIOMUSIC_AM824_COMPOUND = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_AUDIOMUSIC_AM824_COMPOUND, + eFHL1_AUDIOMUSIC_DONT_CARE = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_1_AUDIOMUSIC_DONT_CARE, + }; + + enum EFormatHierarchyLevel2 { + eFHL2_AM824_IEC60968_3 = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_IEC60968_3, + eFHL2_AM824_IEC61937_3 = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_IEC61937_3, + eFHL2_AM824_IEC61937_4 = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_IEC61937_4, + eFHL2_AM824_IEC61937_5 = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_IEC61937_5, + eFHL2_AM824_IEC61937_6 = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_IEC61937_6, + eFHL2_AM824_IEC61937_7 = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_IEC61937_7, + eFHL2_AM824_MULTI_BIT_LINEAR_AUDIO_RAW = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_MULTI_BIT_LINEAR_AUDIO_RAW, + eFHL2_AM824_MULTI_BIT_LINEAR_AUDIO_DVD_AUDIO = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_MULTI_BIT_LINEAR_AUDIO_DVD_AUDIO, + eFHL2_AM824_ONE_BIT_AUDIO_PLAIN_RAW = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_ONE_BIT_AUDIO_PLAIN_RAW, + eFHL2_AM824_ONE_BIT_AUDIO_PLAIN_SACD = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_ONE_BIT_AUDIO_PLAIN_SACD, + eFHL2_AM824_ONE_BIT_AUDIO_ENCODED_RAW = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_ONE_BIT_AUDIO_ENCODED_RAW, + eFHL2_AM824_ONE_BIT_AUDIO_ENCODED_SACD = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_ONE_BIT_AUDIO_ENCODED_SACD, + eFHL2_AM824_HIGH_PRECISION_MULTIBIT_LINEAR_AUDIO = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_HIGH_PRECISION_MULTIBIT_LINEAR_AUDIO, + eFHL2_AM824_MIDI_CONFORMANT = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_MIDI_CONFORMANT, + eFHL2_AM824_SYNC_STREAM = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_SYNC_STREAM, + eFHL2_AM824_DONT_CARE = AVC1394_STREAM_FORMAT_HIERARCHY_LEVEL_2_AM824_DONT_CARE, + }; + + typedef byte_t format_hierarchy_root_t; + typedef byte_t format_hierarchy_level1_t; + typedef byte_t format_hierarchy_level2_t; + + FormatInformation(); + FormatInformation( const FormatInformation& rhs ); + virtual ~FormatInformation(); + + virtual bool serialize( Util::IOSSerialize& se ); + virtual bool deserialize( Util::IISDeserialize& de ); + + virtual FormatInformation* clone() const; + + format_hierarchy_root_t m_root; + format_hierarchy_level1_t m_level1; + format_hierarchy_level2_t m_level2; + FormatInformationStreams* m_streams; +}; + +/////////////////////////////////////////////////////////// + +class ExtendedStreamFormatCmd: public AVCCommand +{ +public: + enum ESubFunction { + eSF_Input = AVC1394_STREAM_FORMAT_SUBFUNCTION_INPUT, + eSF_Output = AVC1394_STREAM_FORMAT_SUBFUNCTION_OUTPUT, + eSF_ExtendedStreamFormatInformationCommand = AVC1394_STREAM_FORMAT_SUBFUNCTION_EXTENDED_STREAM_FORMAT, + eSF_ExtendedStreamFormatInformationCommandList = AVC1394_STREAM_FORMAT_SUBFUNCTION_EXTENDED_STREAM_FORMAT_LIST, + }; + + enum EStatus { + eS_Active = AVC1394_EXTENDED_STREAM_FORMAT_INFO_STATUS_ACTIVE, + eS_Inactive = AVC1394_EXTENDED_STREAM_FORMAT_INFO_STATUS_INACTIVE, + eS_NoStreamFormat = AVC1394_EXTENDED_STREAM_FORMAT_INFO_STATUS_NO_STREAM_FORMAT, + eS_NotUsed = AVC1394_EXTENDED_STREAM_FORMAT_INFO_STATUS_NOT_USED, + }; + typedef byte_t status_t; + typedef byte_t index_in_stream_format_t; + + ExtendedStreamFormatCmd( Ieee1394Service& ieee1349service, ESubFunction eSubFunction = eSF_ExtendedStreamFormatInformationCommand ); + ExtendedStreamFormatCmd( const ExtendedStreamFormatCmd& rhs ); + virtual ~ExtendedStreamFormatCmd(); + + bool setPlugAddress( const PlugAddress& plugAddress ); + bool setIndexInStreamFormat( const int index ); + bool setSubFunction( ESubFunction subFunction ); + + virtual bool serialize( Util::IOSSerialize& se ); + virtual bool deserialize( Util::IISDeserialize& de ); + + EStatus getStatus(); + FormatInformation* getFormatInformation(); + index_in_stream_format_t getIndex(); + + virtual const char* getCmdName() const + { return "ExtendedStreamFormatCmd"; } + +protected: + subfunction_t m_subFunction; + PlugAddress* m_plugAddress; + status_t m_status; + index_in_stream_format_t m_indexInStreamFormat; + FormatInformation* m_formatInformation; +}; + +} + +#endif // AVCEXTENDEDSTREAMFROMAT_H Index: /branches/api-cleanup/src/libavc/streamformat/avc_extended_stream_format.cpp =================================================================== --- /branches/api-cleanup/src/libavc/streamformat/avc_extended_stream_format.cpp (revision 742) +++ /branches/api-cleanup/src/libavc/streamformat/avc_extended_stream_format.cpp (revision 742) @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "avc_extended_stream_format.h" +#include "libutil/cmd_serialize.h" +#include "libieee1394/ieee1394service.h" + +#include + +namespace AVC { + +/////////////////////////////////////////////////////////// +std::ostream& operator<<( std::ostream& stream, StreamFormatInfo info ) +{ +/* stream << info.m_freq << " Hz ("; + if ( info.m_format ) { + stream << "sync "; + } else { + stream << "compound "; + } + stream << "stream, "; + stream << "audio channels: " << info.m_audioChannels + << ", midi channels: " << info.m_midiChannels << ")"; +*/ + stream << " NbChannels " << (int)info.m_numberOfChannels << ", Format " << (int)info.m_streamFormat; + return stream; +} + +StreamFormatInfo::StreamFormatInfo() + : IBusData() +{ +} + +bool +StreamFormatInfo::serialize( Util::IOSSerialize& se ) +{ + se.write( m_numberOfChannels, "StreamFormatInfo numberOfChannels" ); + se.write( m_streamFormat, "StreamFormatInfo streamFormat" ); + return true; +} + +bool +StreamFormatInfo::deserialize( Util::IISDeserialize& de ) +{ + de.read( &m_numberOfChannels ); + de.read( &m_streamFormat ); + return true; +} + +StreamFormatInfo* +StreamFormatInfo::clone() const +{ + return new StreamFormatInfo( *this ); +} + +//////////////////////////////////////////////////////////// + +FormatInformationStreamsSync::FormatInformationStreamsSync() + : FormatInformationStreams() + , m_reserved0( 0xff ) + , m_samplingFrequency( eSF_DontCare ) + , m_rateControl( eRC_DontCare ) + , m_reserved1( 0xff ) +{ +} + +bool +FormatInformationStreamsSync::serialize( Util::IOSSerialize& se ) +{ + se.write( m_reserved0, "FormatInformationStreamsSync reserved" ); + + // we have to clobber some bits + byte_t operand = ( m_samplingFrequency << 4 ) | 0x0e; + if ( m_rateControl == eRC_DontCare) { + operand |= 0x1; + } + se.write( operand, "FormatInformationStreamsSync sampling frequency and rate control" ); + + se.write( m_reserved1, "FormatInformationStreamsSync reserved" ); + return true; +} + +bool +FormatInformationStreamsSync::deserialize( Util::IISDeserialize& de ) +{ + de.read( &m_reserved0 ); + + byte_t operand; + de.read( &operand ); + m_samplingFrequency = operand >> 4; + m_rateControl = operand & 0x01; + + de.read( &m_reserved1 ); + return true; +} + +FormatInformationStreamsSync* +FormatInformationStreamsSync::clone() const +{ + return new FormatInformationStreamsSync( *this ); +} + +//////////////////////////////////////////////////////////// +std::ostream& operator<<( std::ostream& stream, FormatInformationStreamsCompound info ) +{ + stream << (int)info.m_samplingFrequency << " Hz (rate control: "; + stream << (int)info.m_rateControl << ")" << std::endl; + + for ( FormatInformationStreamsCompound::StreamFormatInfoVector::iterator it = info.m_streamFormatInfos.begin(); + it != info.m_streamFormatInfos.end(); + ++it ) + { + StreamFormatInfo* sfi=*it; + stream << " > " << *sfi << std::endl; + } + + return stream; +} + +FormatInformationStreamsCompound::FormatInformationStreamsCompound() + : FormatInformationStreams() + , m_samplingFrequency( eSF_DontCare ) + , m_rateControl( eRC_DontCare ) + , m_numberOfStreamFormatInfos( 0 ) +{ +} + +FormatInformationStreamsCompound::~FormatInformationStreamsCompound() +{ + for ( StreamFormatInfoVector::iterator it = m_streamFormatInfos.begin(); + it != m_streamFormatInfos.end(); + ++it ) + { + delete *it; + } +} + +bool +FormatInformationStreamsCompound::serialize( Util::IOSSerialize& se ) +{ + se.write( m_samplingFrequency, "FormatInformationStreamsCompound samplingFrequency" ); + se.write( m_rateControl, "FormatInformationStreamsCompound rateControl" ); + se.write( m_numberOfStreamFormatInfos, "FormatInformationStreamsCompound numberOfStreamFormatInfos" ); + for ( StreamFormatInfoVector::iterator it = m_streamFormatInfos.begin(); + it != m_streamFormatInfos.end(); + ++it ) + { + ( *it )->serialize( se ); + } + return true; +} + +bool +FormatInformationStreamsCompound::deserialize( Util::IISDeserialize& de ) +{ + de.read( &m_samplingFrequency ); + de.read( &m_rateControl ); + de.read( &m_numberOfStreamFormatInfos ); + for ( int i = 0; i < m_numberOfStreamFormatInfos; ++i ) { + StreamFormatInfo* streamFormatInfo = new StreamFormatInfo; + if ( !streamFormatInfo->deserialize( de ) ) { + return false; + } + m_streamFormatInfos.push_back( streamFormatInfo ); + } + return true; +} + +FormatInformationStreamsCompound* +FormatInformationStreamsCompound::clone() const +{ + return new FormatInformationStreamsCompound( *this ); +} + +//////////////////////////////////////////////////////////// + +FormatInformation::FormatInformation() + : IBusData() + , m_root( eFHR_Invalid ) + , m_level1( eFHL1_AUDIOMUSIC_DONT_CARE ) + , m_level2( eFHL2_AM824_DONT_CARE ) + , m_streams( 0 ) +{ +} + +FormatInformation::FormatInformation( const FormatInformation& rhs ) + : IBusData() + , m_root( rhs.m_root ) + , m_level1( rhs.m_level1 ) + , m_level2( rhs.m_level2 ) + , m_streams( 0 ) +{ + if ( rhs.m_streams ) { + m_streams = dynamic_cast( rhs.m_streams->clone() ); + } +} + +FormatInformation::~FormatInformation() +{ + delete m_streams; + m_streams = 0; +} + +bool +FormatInformation::serialize( Util::IOSSerialize& se ) +{ + if ( m_root != eFHR_Invalid ) { + se.write( m_root, "FormatInformation hierarchy root" ); + if ( m_level1 != eFHL1_AUDIOMUSIC_DONT_CARE ) { + se.write( m_level1, "FormatInformation hierarchy level 1" ); + if ( m_level2 != eFHL2_AM824_DONT_CARE ) { + se.write( m_level2, "FormatInformation hierarchy level 2" ); + } + } + } + if ( m_streams ) { + return m_streams->serialize( se ); + } + return true; +} + +bool +FormatInformation::deserialize( Util::IISDeserialize& de ) +{ + bool result = false; + + delete m_streams; + m_streams = 0; + + // this code just parses BeBoB replies. + de.read( &m_root ); + + // just parse an audio and music hierarchy + if ( m_root == eFHR_AudioMusic ) { + de.read( &m_level1 ); + + switch ( m_level1 ) { + case eFHL1_AUDIOMUSIC_AM824: + { + // note: compound streams don't have a second level + de.read( &m_level2 ); + + if (m_level2 == eFHL2_AM824_SYNC_STREAM ) { + m_streams = new FormatInformationStreamsSync(); + result = m_streams->deserialize( de ); + } else { + // anything but the sync stream workds currently. + printf( "could not parse format information. (format hierarchy level 2 not recognized)\n" ); + } + } + break; + case eFHL1_AUDIOMUSIC_AM824_COMPOUND: + { + m_streams = new FormatInformationStreamsCompound(); + result = m_streams->deserialize( de ); + } + break; + default: + printf( "could not parse format information. (format hierarchy level 1 not recognized)\n" ); + } + } + + return result; +} + +FormatInformation* +FormatInformation::clone() const +{ + return new FormatInformation( *this ); +} + +//////////////////////////////////////////////////////////// + +ExtendedStreamFormatCmd::ExtendedStreamFormatCmd( Ieee1394Service& service, + ESubFunction eSubFunction ) + : AVCCommand( service, AVC1394_STREAM_FORMAT_SUPPORT ) + , m_subFunction( eSubFunction ) + , m_status( eS_NotUsed ) + , m_indexInStreamFormat( 0 ) + , m_formatInformation( new FormatInformation ) +{ + UnitPlugAddress unitPlugAddress( UnitPlugAddress::ePT_PCR, 0x00 ); + m_plugAddress = new PlugAddress( PlugAddress::ePD_Output, PlugAddress::ePAM_Unit, unitPlugAddress ); +} + +ExtendedStreamFormatCmd::ExtendedStreamFormatCmd( + const ExtendedStreamFormatCmd& rhs ) + : AVCCommand( rhs ) +{ + m_subFunction = rhs.m_subFunction; + m_plugAddress = new PlugAddress( *rhs.m_plugAddress ); + m_formatInformation = + new FormatInformation( *rhs.m_formatInformation ); +} + +ExtendedStreamFormatCmd::~ExtendedStreamFormatCmd() +{ + delete m_plugAddress; + m_plugAddress = 0; + delete m_formatInformation; + m_formatInformation = 0; +} + +bool +ExtendedStreamFormatCmd::setPlugAddress( const PlugAddress& plugAddress ) +{ + delete m_plugAddress; + m_plugAddress = plugAddress.clone(); + return true; +} + +bool +ExtendedStreamFormatCmd::setIndexInStreamFormat( const int index ) +{ + m_indexInStreamFormat = index; + return true; +} + +bool +ExtendedStreamFormatCmd::serialize( Util::IOSSerialize& se ) +{ + AVCCommand::serialize( se ); + se.write( m_subFunction, "ExtendedStreamFormatCmd subFunction" ); + m_plugAddress->serialize( se ); + se.write( m_status, "ExtendedStreamFormatCmd status" ); + if ( m_subFunction == eSF_ExtendedStreamFormatInformationCommandList ) { + se.write( m_indexInStreamFormat, "indexInStreamFormat" ); + } + m_formatInformation->serialize( se ); + return true; +} + +bool +ExtendedStreamFormatCmd::deserialize( Util::IISDeserialize& de ) +{ + AVCCommand::deserialize( de ); + de.read( &m_subFunction ); + m_plugAddress->deserialize( de ); + de.read( &m_status ); + if ( m_subFunction == eSF_ExtendedStreamFormatInformationCommandList ) { + de.read( &m_indexInStreamFormat ); + } + m_formatInformation->deserialize( de ); + return true; +} + +ExtendedStreamFormatCmd::EStatus +ExtendedStreamFormatCmd::getStatus() +{ + EStatus status = static_cast( m_status ); + return status; +} + +FormatInformation* +ExtendedStreamFormatCmd::getFormatInformation() +{ + return m_formatInformation; +} + +ExtendedStreamFormatCmd::index_in_stream_format_t +ExtendedStreamFormatCmd::getIndex() +{ + return m_indexInStreamFormat; +} + +bool +ExtendedStreamFormatCmd::setSubFunction( ESubFunction subFunction ) +{ + m_subFunction = subFunction; + return true; +} + +} Index: /branches/api-cleanup/src/libavc/avc_definitions.cpp =================================================================== --- /branches/api-cleanup/src/libavc/avc_definitions.cpp (revision 742) +++ /branches/api-cleanup/src/libavc/avc_definitions.cpp (revision 742) @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "avc_definitions.h" +#include + +namespace AVC { + +int +convertESamplingFrequency(ESamplingFrequency freq) +{ + int value = 0; + + switch ( freq ) { + case eSF_22050Hz: + value = 22050; + break; + case eSF_24000Hz: + value = 24000; + break; + case eSF_32000Hz: + value = 32000; + break; + case eSF_44100Hz: + value = 44100; + break; + case eSF_48000Hz: + value = 48000; + break; + case eSF_88200Hz: + value = 88200; + break; + case eSF_96000Hz: + value = 96000; + break; + case eSF_176400Hz: + value = 176400; + break; + case eSF_192000Hz: + value = 192000; + break; + case eSF_AnyLow: + value = 48000; + break; + case eSF_AnyMid: + value = 96000; + break; + case eSF_AnyHigh: + value = 192000; + break; + default: + value = 0; + } + + + return value; +} + +ESamplingFrequency +parseSampleRate( int sampleRate ) +{ + ESamplingFrequency efreq; + switch ( sampleRate ) { + case 22050: + efreq = eSF_22050Hz; + break; + case 24000: + efreq = eSF_24000Hz; + break; + case 32000: + efreq = eSF_32000Hz; + break; + case 44100: + efreq = eSF_44100Hz; + break; + case 48000: + efreq = eSF_48000Hz; + break; + case 88200: + efreq = eSF_88200Hz; + break; + case 96000: + efreq = eSF_96000Hz; + break; + case 176400: + efreq = eSF_176400Hz; + break; + case 192000: + efreq = eSF_192000Hz; + break; + default: + efreq = eSF_DontCare; + } + + return efreq; +} + +std::ostream& operator<<( std::ostream& stream, ESamplingFrequency samplingFrequency ) +{ + char* str; + switch ( samplingFrequency ) { + case eSF_22050Hz: + str = "22050"; + break; + case eSF_24000Hz: + str = "24000"; + break; + case eSF_32000Hz: + str = "32000"; + break; + case eSF_44100Hz: + str = "44100"; + break; + case eSF_48000Hz: + str = "48000"; + break; + case eSF_88200Hz: + str = "88200"; + break; + case eSF_96000Hz: + str = "96000"; + break; + case eSF_176400Hz: + str = "176400"; + break; + case eSF_192000Hz: + str = "192000"; + break; + case eSF_DontCare: + default: + str = "unknown"; + } + return stream << str; +}; + +enum ESubunitType byteToSubunitType(byte_t s) { + switch (s) { + case eST_Monitor: + return eST_Monitor; + case eST_Audio: + return eST_Audio; + case eST_Printer: + return eST_Printer; + case eST_Disc: + return eST_Disc; + case eST_VCR: + return eST_VCR; + case eST_Tuner: + return eST_Tuner; + case eST_CA: + return eST_CA; + case eST_Camera: + return eST_Camera; + case eST_Panel: + return eST_Panel; + case eST_BulltinBoard: + return eST_BulltinBoard; + case eST_CameraStorage: + return eST_CameraStorage; + case eST_Music: + return eST_Music; + case eST_VendorUnique: + return eST_VendorUnique; + case eST_Reserved: + return eST_Reserved; + case eST_Extended: + return eST_Extended; + default: + case eST_Unit: + return eST_Unit; + } +} + +unsigned int fdfSfcToSampleRate(byte_t fdf) { + switch(fdf & 0x07) { + default: return 0; + case IEC61883_FDF_SFC_32KHZ: return 32000; + case IEC61883_FDF_SFC_44K1HZ: return 44100; + case IEC61883_FDF_SFC_48KHZ: return 48000; + case IEC61883_FDF_SFC_88K2HZ: return 88200; + case IEC61883_FDF_SFC_96KHZ: return 96000; + case IEC61883_FDF_SFC_176K4HZ: return 176400; + case IEC61883_FDF_SFC_192KHZ: return 192000; + } +} + +byte_t sampleRateToFdfSfc(unsigned int rate) { + switch(rate) { + default: return 0x07; + 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; + } +} + +} Index: /branches/api-cleanup/src/libavc/avc_definitions.h =================================================================== --- /branches/api-cleanup/src/libavc/avc_definitions.h (revision 742) +++ /branches/api-cleanup/src/libavc/avc_definitions.h (revision 742) @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2005-2007 by Daniel Wagner + * + * This file is part of FFADO + * FFADO = Free Firewire (pro-)audio drivers for linux + * + * FFADO is based upon FreeBoB + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef AVCDEFINITIONS_H +#define AVCDEFINITIONS_H + +#include +#include + + +namespace AVC { + +typedef byte_t ctype_t; +typedef byte_t unit_t; +typedef byte_t unit_type_t; +typedef byte_t subunit_t; +typedef byte_t opcode_t; +typedef byte_t plug_type_t; +typedef byte_t plug_id_t; +typedef byte_t reserved_t; +typedef byte_t function_block_type_t; +typedef byte_t function_block_id_t; +typedef byte_t control_attribute_t; +typedef byte_t plug_direction_t; +typedef byte_t plug_address_mode_t; +typedef byte_t status_t; +typedef byte_t number_of_channels_t; +typedef byte_t stream_format_t; +typedef byte_t sampling_frequency_t; +typedef byte_t rate_control_t; +typedef byte_t number_of_stream_format_infos_t; +typedef byte_t nr_of_plugs_t; +typedef byte_t subunit_id_t; +typedef byte_t subfunction_t; +typedef byte_t opcode_t; +typedef byte_t page_t; +typedef byte_t extension_code_t; +typedef byte_t subunit_type_t; +typedef byte_t max_subunit_id_t; +typedef byte_t nr_of_channels_t; +typedef byte_t stream_position_t; +typedef byte_t stream_position_location_t; +typedef byte_t nr_of_clusters_t; +typedef byte_t string_length_t; +typedef byte_t cluster_index_t; +typedef byte_t port_type_t; +typedef byte_t number_of_output_plugs_t; +typedef byte_t function_block_type_t; +typedef byte_t function_block_id_t; +typedef byte_t function_block_special_purpose_t; +typedef byte_t no_of_input_plugs_t; +typedef byte_t no_of_output_plugs_t; +typedef byte_t info_type_t; +typedef byte_t audio_channel_number_t; +typedef byte_t selector_length_t; +typedef byte_t control_selector_t; +typedef byte_t control_data_length_t; +typedef uint16_t control_data_ext_length_t; +typedef uint16_t mixer_level_t; +typedef byte_t mixer_programmable_state_t; +typedef byte_t input_fb_plug_number_t; +typedef byte_t input_audio_channel_number_t; +typedef byte_t output_audio_channel_number_t; +typedef byte_t status_selector_t; + +typedef quadlet_t company_id_t; + +#define AVC1394_SUBUNIT_AUDIO 1 +#define AVC1394_SUBUNIT_PRINTER 2 +#define AVC1394_SUBUNIT_CA 6 +#define AVC1394_SUBUNIT_PANEL 9 +#define AVC1394_SUBUNIT_BULLETIN_BOARD 0xA +#define AVC1394_SUBUNIT_CAMERA_STORAGE 0xB +#define AVC1394_SUBUNIT_MUSIC 0xC +#define AVC1394_SUBUNIT_RESERVED 0x1D + +#define AVC1394_SUBUNIT_ID_RESERVED 0x06 + +enum ESubunitType { + eST_Monitor = AVC1394_SUBUNIT_VIDEO_MONITOR, + eST_Audio = AVC1394_SUBUNIT_AUDIO, + eST_Printer = AVC1394_SUBUNIT_PRINTER, + eST_Disc = AVC1394_SUBUNIT_DISC_RECORDER, + eST_VCR = AVC1394_SUBUNIT_VCR, + eST_Tuner = AVC1394_SUBUNIT_TUNER, + eST_CA = AVC1394_SUBUNIT_CA, + eST_Camera = AVC1394_SUBUNIT_VIDEO_CAMERA, + eST_Panel = AVC1394_SUBUNIT_PANEL, + eST_BulltinBoard = AVC1394_SUBUNIT_BULLETIN_BOARD, + eST_CameraStorage = AVC1394_SUBUNIT_CAMERA_STORAGE, + eST_Music = AVC1394_SUBUNIT_MUSIC, + eST_VendorUnique = AVC1394_SUBUNIT_VENDOR_UNIQUE, + eST_Reserved = AVC1394_SUBUNIT_RESERVED, + eST_Extended = AVC1394_SUBUNIT_EXTENDED, + eST_Unit = AVC1394_SUBUNIT_UNIT, +}; + +enum ESubunitType byte